xref: /openbmc/libpldm/src/msgbuf.h (revision f3e0a2da)
1 #ifndef PLDM_MSGBUF_H
2 #define PLDM_MSGBUF_H
3 
4 #ifdef __cplusplus
5 /*
6  * Fix up C11's _Static_assert() vs C++'s static_assert().
7  *
8  * Can we please have nice things for once.
9  */
10 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
11 #define _Static_assert(...) static_assert(__VA_ARGS__)
12 extern "C" {
13 #endif
14 
15 #include "base.h"
16 #include "pldm_types.h"
17 
18 #include <assert.h>
19 #include <endian.h>
20 #include <limits.h>
21 #include <stdbool.h>
22 #include <string.h>
23 #include <sys/types.h>
24 
25 struct pldm_msgbuf {
26 	uint8_t *cursor;
27 	ssize_t remaining;
28 };
29 
30 /**
31  * @brief Initialize pldm buf struct for buf extractor
32  *
33  * @param[out] ctx - pldm_msgbuf context for extractor
34  * @param[in] minsize - The minimum required length of buffer `buf`
35  * @param[in] buf - buffer to be extracted
36  * @param[in] len - size of buffer
37  *
38  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
39  * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
40  * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
41  */
42 __attribute__((no_sanitize("pointer-overflow"))) static inline int
43 pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
44 		 size_t len)
45 {
46 	uint8_t *end;
47 
48 	if (!ctx || !buf) {
49 		return PLDM_ERROR_INVALID_DATA;
50 	}
51 
52 	if ((minsize > len) || (len > SSIZE_MAX)) {
53 		return PLDM_ERROR_INVALID_LENGTH;
54 	}
55 
56 	end = (uint8_t *)buf + len;
57 	if (end && end < (uint8_t *)buf) {
58 		return PLDM_ERROR_INVALID_LENGTH;
59 	}
60 
61 	ctx->cursor = (uint8_t *)buf;
62 	ctx->remaining = (ssize_t)len;
63 
64 	return PLDM_SUCCESS;
65 }
66 
67 /**
68  * @brief Validate buffer overflow state
69  *
70  * @param[in] ctx - pldm_msgbuf context for extractor
71  *
72  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
73  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
74  * prior accesses would have occurred beyond the bounds of the buffer, and
75  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
76  * pointer.
77  */
78 static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
79 {
80 	if (!ctx) {
81 		return PLDM_ERROR_INVALID_DATA;
82 	}
83 
84 	return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
85 }
86 
87 /**
88  * @brief Test whether a message buffer has been exactly consumed
89  *
90  * @param[in] ctx - pldm_msgbuf context for extractor
91  *
92  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
93  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
94  * indicates that an incorrect sequence of accesses have occurred, and
95  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
96  * pointer.
97  */
98 static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
99 {
100 	if (!ctx) {
101 		return PLDM_ERROR_INVALID_DATA;
102 	}
103 
104 	return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
105 }
106 
107 /**
108  * @brief Destroy the pldm buf
109  *
110  * @param[in] ctx - pldm_msgbuf context for extractor
111  *
112  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
113  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
114  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
115  * bounds of the buffer.
116  */
117 static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
118 {
119 	int valid;
120 
121 	if (!ctx) {
122 		return PLDM_ERROR_INVALID_DATA;
123 	}
124 
125 	valid = pldm_msgbuf_validate(ctx);
126 
127 	ctx->cursor = NULL;
128 	ctx->remaining = 0;
129 
130 	return valid;
131 }
132 
133 /**
134  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
135  * has been completely consumed without overflow
136  *
137  * @param[in] ctx - pldm_msgbuf context
138  *
139  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
140  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
141  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
142  * have occurred byond the bounds of the buffer
143  */
144 static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
145 {
146 	int consumed;
147 
148 	if (!ctx) {
149 		return PLDM_ERROR_INVALID_DATA;
150 	}
151 
152 	consumed = pldm_msgbuf_consumed(ctx);
153 
154 	ctx->cursor = NULL;
155 	ctx->remaining = 0;
156 
157 	return consumed;
158 }
159 
160 /**
161  * @brief pldm_msgbuf extractor for a uint8_t
162  *
163  * @param[inout] ctx - pldm_msgbuf context for extractor
164  * @param[out] dst - destination of extracted value
165  *
166  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
167  * PLDM_ERROR_INVALID_LENGTH otherwise.
168  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
169  */
170 static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx,
171 					    uint8_t *dst)
172 {
173 	if (!ctx || !ctx->cursor || !dst) {
174 		return PLDM_ERROR_INVALID_DATA;
175 	}
176 
177 	ctx->remaining -= sizeof(*dst);
178 	assert(ctx->remaining >= 0);
179 	if (ctx->remaining < 0) {
180 		return PLDM_ERROR_INVALID_LENGTH;
181 	}
182 
183 	*dst = *((uint8_t *)(ctx->cursor));
184 	ctx->cursor++;
185 	return PLDM_SUCCESS;
186 }
187 
188 static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_t *dst)
189 {
190 	if (!ctx || !ctx->cursor || !dst) {
191 		return PLDM_ERROR_INVALID_DATA;
192 	}
193 
194 	ctx->remaining -= sizeof(*dst);
195 	assert(ctx->remaining >= 0);
196 	if (ctx->remaining < 0) {
197 		return PLDM_ERROR_INVALID_LENGTH;
198 	}
199 
200 	*dst = *((int8_t *)(ctx->cursor));
201 	ctx->cursor++;
202 	return PLDM_SUCCESS;
203 }
204 
205 static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
206 					     uint16_t *dst)
207 {
208 	uint16_t ldst;
209 
210 	if (!ctx || !ctx->cursor || !dst) {
211 		return PLDM_ERROR_INVALID_DATA;
212 	}
213 
214 	// Check for buffer overflow. If we overflow, account for the request as
215 	// negative values in ctx->remaining. This way we can debug how far
216 	// we've overflowed.
217 	ctx->remaining -= sizeof(ldst);
218 
219 	// Prevent the access if it would overflow. First, assert so we blow up
220 	// the test suite right at the point of failure. However, cater to
221 	// -DNDEBUG by explicitly testing that the access is valid.
222 	assert(ctx->remaining >= 0);
223 	if (ctx->remaining < 0) {
224 		return PLDM_ERROR_INVALID_LENGTH;
225 	}
226 
227 	// Use memcpy() to have the compiler deal with any alignment
228 	// issues on the target architecture
229 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
230 
231 	// Only assign the target value once it's correctly decoded
232 	*dst = le16toh(ldst);
233 	ctx->cursor += sizeof(ldst);
234 
235 	return PLDM_SUCCESS;
236 }
237 
238 static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx,
239 					    int16_t *dst)
240 {
241 	int16_t ldst;
242 
243 	if (!ctx || !ctx->cursor || !dst) {
244 		return PLDM_ERROR_INVALID_DATA;
245 	}
246 
247 	ctx->remaining -= sizeof(ldst);
248 	assert(ctx->remaining >= 0);
249 	if (ctx->remaining < 0) {
250 		return PLDM_ERROR_INVALID_LENGTH;
251 	}
252 
253 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
254 
255 	*dst = le16toh(ldst);
256 	ctx->cursor += sizeof(ldst);
257 
258 	return PLDM_SUCCESS;
259 }
260 
261 static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
262 					     uint32_t *dst)
263 {
264 	uint32_t ldst;
265 
266 	if (!ctx || !ctx->cursor || !dst) {
267 		return PLDM_ERROR_INVALID_DATA;
268 	}
269 
270 	ctx->remaining -= sizeof(ldst);
271 	assert(ctx->remaining >= 0);
272 	if (ctx->remaining < 0) {
273 		return PLDM_ERROR_INVALID_LENGTH;
274 	}
275 
276 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
277 
278 	*dst = le32toh(ldst);
279 	ctx->cursor += sizeof(ldst);
280 
281 	return PLDM_SUCCESS;
282 }
283 
284 static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx,
285 					    int32_t *dst)
286 {
287 	int32_t ldst;
288 
289 	if (!ctx || !ctx->cursor || !dst) {
290 		return PLDM_ERROR_INVALID_DATA;
291 	}
292 
293 	ctx->remaining -= sizeof(ldst);
294 	assert(ctx->remaining >= 0);
295 	if (ctx->remaining < 0) {
296 		return PLDM_ERROR_INVALID_LENGTH;
297 	}
298 
299 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
300 
301 	*dst = le32toh(ldst);
302 	ctx->cursor += sizeof(ldst);
303 
304 	return PLDM_SUCCESS;
305 }
306 
307 static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx,
308 					     real32_t *dst)
309 {
310 	uint32_t ldst;
311 
312 	if (!ctx || !ctx->cursor || !dst) {
313 		return PLDM_ERROR_INVALID_DATA;
314 	}
315 
316 	ctx->remaining -= sizeof(ldst);
317 	assert(ctx->remaining >= 0);
318 	if (ctx->remaining < 0) {
319 		return PLDM_ERROR_INVALID_LENGTH;
320 	}
321 
322 	_Static_assert(sizeof(*dst) == sizeof(ldst),
323 		       "Mismatched type sizes for dst and ldst");
324 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
325 	ldst = le32toh(ldst);
326 	memcpy(dst, &ldst, sizeof(*dst));
327 	ctx->cursor += sizeof(*dst);
328 
329 	return PLDM_SUCCESS;
330 }
331 
332 #define pldm_msgbuf_extract(ctx, dst)                                          \
333 	_Generic((*(dst)),                                                     \
334 		uint8_t: pldm_msgbuf_extract_uint8,                            \
335 		int8_t: pldm_msgbuf_extract_int8,                              \
336 		uint16_t: pldm_msgbuf_extract_uint16,                          \
337 		int16_t: pldm_msgbuf_extract_int16,                            \
338 		uint32_t: pldm_msgbuf_extract_uint32,                          \
339 		int32_t: pldm_msgbuf_extract_int32,                            \
340 		real32_t: pldm_msgbuf_extract_real32)(ctx, dst)
341 
342 static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
343 						  uint8_t *dst, size_t count)
344 {
345 	size_t len;
346 
347 	if (!ctx || !ctx->cursor || !dst) {
348 		return PLDM_ERROR_INVALID_DATA;
349 	}
350 
351 	if (!count) {
352 		return PLDM_SUCCESS;
353 	}
354 
355 	len = sizeof(*dst) * count;
356 	if (len > SSIZE_MAX) {
357 		return PLDM_ERROR_INVALID_LENGTH;
358 	}
359 
360 	ctx->remaining -= (ssize_t)len;
361 	assert(ctx->remaining >= 0);
362 	if (ctx->remaining < 0) {
363 		return PLDM_ERROR_INVALID_LENGTH;
364 	}
365 
366 	memcpy(dst, ctx->cursor, len);
367 	ctx->cursor += len;
368 
369 	return PLDM_SUCCESS;
370 }
371 
372 #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
373 	_Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
374 								     count)
375 
376 static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
377 					    const uint32_t src)
378 {
379 	uint32_t val = htole32(src);
380 
381 	if (!ctx || !ctx->cursor) {
382 		return PLDM_ERROR_INVALID_DATA;
383 	}
384 
385 	ctx->remaining -= sizeof(src);
386 	assert(ctx->remaining >= 0);
387 	if (ctx->remaining < 0) {
388 		return PLDM_ERROR_INVALID_LENGTH;
389 	}
390 
391 	memcpy(ctx->cursor, &val, sizeof(val));
392 	ctx->cursor += sizeof(src);
393 
394 	return PLDM_SUCCESS;
395 }
396 
397 static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
398 					    const uint16_t src)
399 {
400 	uint16_t val = htole16(src);
401 
402 	if (!ctx || !ctx->cursor) {
403 		return PLDM_ERROR_INVALID_DATA;
404 	}
405 
406 	ctx->remaining -= sizeof(src);
407 	assert(ctx->remaining >= 0);
408 	if (ctx->remaining < 0) {
409 		return PLDM_ERROR_INVALID_LENGTH;
410 	}
411 
412 	memcpy(ctx->cursor, &val, sizeof(val));
413 	ctx->cursor += sizeof(src);
414 
415 	return PLDM_SUCCESS;
416 }
417 
418 static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
419 					   const uint8_t src)
420 {
421 	if (!ctx || !ctx->cursor) {
422 		return PLDM_ERROR_INVALID_DATA;
423 	}
424 
425 	ctx->remaining -= sizeof(src);
426 	assert(ctx->remaining >= 0);
427 	if (ctx->remaining < 0) {
428 		return PLDM_ERROR_INVALID_LENGTH;
429 	}
430 
431 	memcpy(ctx->cursor, &src, sizeof(src));
432 	ctx->cursor += sizeof(src);
433 
434 	return PLDM_SUCCESS;
435 }
436 
437 static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
438 					   const int32_t src)
439 {
440 	int32_t val = htole32(src);
441 
442 	if (!ctx || !ctx->cursor) {
443 		return PLDM_ERROR_INVALID_DATA;
444 	}
445 
446 	ctx->remaining -= sizeof(src);
447 	assert(ctx->remaining >= 0);
448 	if (ctx->remaining < 0) {
449 		return PLDM_ERROR_INVALID_LENGTH;
450 	}
451 
452 	memcpy(ctx->cursor, &val, sizeof(val));
453 	ctx->cursor += sizeof(src);
454 
455 	return PLDM_SUCCESS;
456 }
457 
458 static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
459 					   const int16_t src)
460 {
461 	int16_t val = htole16(src);
462 
463 	if (!ctx || !ctx->cursor) {
464 		return PLDM_ERROR_INVALID_DATA;
465 	}
466 
467 	ctx->remaining -= sizeof(src);
468 	assert(ctx->remaining >= 0);
469 	if (ctx->remaining < 0) {
470 		return PLDM_ERROR_INVALID_LENGTH;
471 	}
472 
473 	memcpy(ctx->cursor, &val, sizeof(val));
474 	ctx->cursor += sizeof(src);
475 
476 	return PLDM_SUCCESS;
477 }
478 
479 static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
480 					  const int8_t src)
481 {
482 	if (!ctx || !ctx->cursor) {
483 		return PLDM_ERROR_INVALID_DATA;
484 	}
485 
486 	ctx->remaining -= sizeof(src);
487 	assert(ctx->remaining >= 0);
488 	if (ctx->remaining < 0) {
489 		return PLDM_ERROR_INVALID_LENGTH;
490 	}
491 
492 	memcpy(ctx->cursor, &src, sizeof(src));
493 	ctx->cursor += sizeof(src);
494 
495 	return PLDM_SUCCESS;
496 }
497 
498 #define pldm_msgbuf_insert(dst, src)                                           \
499 	_Generic((src),                                                        \
500 		uint8_t: pldm_msgbuf_insert_uint8,                             \
501 		int8_t: pldm_msgbuf_insert_int8,                               \
502 		uint16_t: pldm_msgbuf_insert_uint16,                           \
503 		int16_t: pldm_msgbuf_insert_int16,                             \
504 		uint32_t: pldm_msgbuf_insert_uint32,                           \
505 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
506 
507 static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
508 						 const uint8_t *src,
509 						 size_t count)
510 {
511 	size_t len;
512 	if (!ctx || !ctx->cursor || !src) {
513 		return PLDM_ERROR_INVALID_DATA;
514 	}
515 
516 	if (!count) {
517 		return PLDM_SUCCESS;
518 	}
519 
520 	len = sizeof(*src) * count;
521 	if (len > SSIZE_MAX) {
522 		return PLDM_ERROR_INVALID_LENGTH;
523 	}
524 
525 	ctx->remaining -= (ssize_t)len;
526 	assert(ctx->remaining >= 0);
527 	if (ctx->remaining < 0) {
528 		return PLDM_ERROR_INVALID_LENGTH;
529 	}
530 
531 	memcpy(ctx->cursor, src, len);
532 	ctx->cursor += len;
533 
534 	return PLDM_SUCCESS;
535 }
536 
537 #define pldm_msgbuf_insert_array(dst, src, count)                              \
538 	_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src,  \
539 								    count)
540 
541 static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
542 					    size_t required, void **cursor)
543 {
544 	if (!ctx || !ctx->cursor || !cursor || *cursor) {
545 		return PLDM_ERROR_INVALID_DATA;
546 	}
547 
548 	if (required > SSIZE_MAX) {
549 		return PLDM_ERROR_INVALID_LENGTH;
550 	}
551 
552 	ctx->remaining -= (ssize_t)required;
553 	assert(ctx->remaining >= 0);
554 	if (ctx->remaining < 0) {
555 		return PLDM_ERROR_INVALID_LENGTH;
556 	}
557 
558 	*cursor = ctx->cursor;
559 	ctx->cursor += required;
560 
561 	return PLDM_SUCCESS;
562 }
563 
564 static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
565 					     void **cursor, size_t *len)
566 {
567 	if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
568 		return PLDM_ERROR_INVALID_DATA;
569 	}
570 
571 	assert(ctx->remaining >= 0);
572 	if (ctx->remaining < 0) {
573 		return PLDM_ERROR_INVALID_LENGTH;
574 	}
575 
576 	*cursor = ctx->cursor;
577 	ctx->cursor += ctx->remaining;
578 	*len = ctx->remaining;
579 	ctx->remaining = 0;
580 
581 	return PLDM_SUCCESS;
582 }
583 #ifdef __cplusplus
584 }
585 #endif
586 
587 #endif /* BUF_H */
588