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