xref: /openbmc/libpldm/src/msgbuf.h (revision 2ff8cf89)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 #ifndef PLDM_MSGBUF_H
3 #define PLDM_MSGBUF_H
4 
5 /*
6  * Historically, many of the structs exposed in libpldm's public headers are
7  * defined with __attribute__((packed)). This is unfortunate: it gives the
8  * impression that a wire-format buffer can be cast to the message type to make
9  * the message's fields easily accessible. As it turns out, that's not
10  * that's valid for several reasons:
11  *
12  * 1. Casting the wire-format buffer to a struct of the message type doesn't
13  *    abstract the endianness of message field values
14  *
15  * 2. Some messages contain packed tagged union fields which cannot be properly
16  *    described in a C struct.
17  *
18  * The msgbuf APIs exist to assist with (un)packing the wire-format in a way
19  * that is type-safe, spatially memory-safe, endian-safe, performant, and
20  * free of undefined-behaviour. Message structs that are added to the public
21  * library API should no-longer be marked __attribute__((packed)), and the
22  * implementation of their encode and decode functions must exploit the msgbuf
23  * API.
24  *
25  * However, we would like to allow implementation of codec functions in terms of
26  * msgbuf APIs even if they're decoding a message into a (historically) packed
27  * struct. Some of the complexity that follows is a consequence of the packed/
28  * unpacked conflict.
29  */
30 
31 #ifdef __cplusplus
32 /*
33  * Fix up C11's _Static_assert() vs C++'s static_assert().
34  *
35  * Can we please have nice things for once.
36  */
37 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
38 #define _Static_assert(...) static_assert(__VA_ARGS__)
39 extern "C" {
40 #endif
41 
42 #include <libpldm/base.h>
43 #include <libpldm/pldm_types.h>
44 
45 #include "compiler.h"
46 
47 #include <assert.h>
48 #include <endian.h>
49 #include <limits.h>
50 #include <stdbool.h>
51 #include <stdint.h>
52 #include <string.h>
53 #include <sys/types.h>
54 
55 /*
56  * We can't use static_assert() outside of some other C construct. Deal
57  * with high-level global assertions by burying them in an unused struct
58  * declaration, that has a sole member for compliance with the requirement that
59  * types must have a size.
60 */
61 static struct {
62 	static_assert(
63 		INTMAX_MAX != SIZE_MAX,
64 		"Extraction and insertion value comparisons may be broken");
65 	static_assert(INTMAX_MIN + INTMAX_MAX <= 0,
66 		      "Extraction and insertion arithmetic may be broken");
67 	int compliance;
68 } build_assertions __attribute__((unused));
69 
70 struct pldm_msgbuf {
71 	uint8_t *cursor;
72 	intmax_t remaining;
73 };
74 
75 /**
76  * @brief Initialize pldm buf struct for buf extractor
77  *
78  * @param[out] ctx - pldm_msgbuf context for extractor
79  * @param[in] minsize - The minimum required length of buffer `buf`
80  * @param[in] buf - buffer to be extracted
81  * @param[in] len - size of buffer
82  *
83  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
84  * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
85  * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
86  */
87 static inline int pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize,
88 				   const void *buf, size_t len)
89 {
90 	if (!ctx || !buf) {
91 		return PLDM_ERROR_INVALID_DATA;
92 	}
93 
94 	if ((minsize > len)) {
95 		return PLDM_ERROR_INVALID_LENGTH;
96 	}
97 
98 #if INTMAX_MAX < SIZE_MAX
99 	if (len > INTMAX_MAX) {
100 		return PLDM_ERROR_INVALID_LENGTH;
101 	}
102 #endif
103 
104 	if ((uintptr_t)buf + len < len) {
105 		return PLDM_ERROR_INVALID_LENGTH;
106 	}
107 
108 	ctx->cursor = (uint8_t *)buf;
109 	ctx->remaining = (intmax_t)len;
110 
111 	return PLDM_SUCCESS;
112 }
113 
114 /**
115  * @brief Validate buffer overflow state
116  *
117  * @param[in] ctx - pldm_msgbuf context for extractor
118  *
119  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
120  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
121  * prior accesses would have occurred beyond the bounds of the buffer, and
122  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
123  * pointer.
124  */
125 static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
126 {
127 	if (!ctx) {
128 		return PLDM_ERROR_INVALID_DATA;
129 	}
130 
131 	return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
132 }
133 
134 /**
135  * @brief Test whether a message buffer has been exactly consumed
136  *
137  * @param[in] ctx - pldm_msgbuf context for extractor
138  *
139  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
140  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
141  * indicates that an incorrect sequence of accesses have occurred, and
142  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
143  * pointer.
144  */
145 static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
146 {
147 	if (!ctx) {
148 		return PLDM_ERROR_INVALID_DATA;
149 	}
150 
151 	return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
152 }
153 
154 /**
155  * @brief Destroy the pldm buf
156  *
157  * @param[in] ctx - pldm_msgbuf context for extractor
158  *
159  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
160  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
161  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
162  * bounds of the buffer.
163  */
164 static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
165 {
166 	int valid;
167 
168 	if (!ctx) {
169 		return PLDM_ERROR_INVALID_DATA;
170 	}
171 
172 	valid = pldm_msgbuf_validate(ctx);
173 
174 	ctx->cursor = NULL;
175 	ctx->remaining = 0;
176 
177 	return valid;
178 }
179 
180 /**
181  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
182  * has been completely consumed without overflow
183  *
184  * @param[in] ctx - pldm_msgbuf context
185  *
186  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
187  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
188  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
189  * have occurred byond the bounds of the buffer
190  */
191 static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
192 {
193 	int consumed;
194 
195 	if (!ctx) {
196 		return PLDM_ERROR_INVALID_DATA;
197 	}
198 
199 	consumed = pldm_msgbuf_consumed(ctx);
200 
201 	ctx->cursor = NULL;
202 	ctx->remaining = 0;
203 
204 	return consumed;
205 }
206 
207 /*
208  * Exploit the pre-processor to perform type checking by macro substitution.
209  *
210  * A C type is defined by its alignment as well as its object
211  * size, and compilers have a hammer to enforce it in the form of
212  * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
213  * the libpldm public API this presents a problem: Naively attempting to use the
214  * msgbuf APIs on a member of a packed struct would yield an error.
215  *
216  * The msgbuf APIs are implemented such that data is moved through unaligned
217  * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
218  * make the object pointers take a trip through `void *` at its API boundary.
219  * That presents a bit too much of an opportunity to non-surgically remove your
220  * own foot, so here we set about doing something to mitigate that as well.
221  *
222  * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
223  * only for the purpose of object sizes, disregarding alignment. We have a few
224  * constraints that cause some headaches:
225  *
226  * 1. We have to perform the type-check before a call through a C function,
227  *    as the function must take the object pointer argument as `void *`.
228  *    Essentially, this constrains us to doing something with macros.
229  *
230  * 2. While libpldm is a C library, its test suite is written in C++ to take
231  *    advantage of gtest.
232  *
233  * 3. Ideally we'd do something with C's `static_assert()`, however
234  *    `static_assert()` is defined as void, and as we're constrained to macros,
235  *    using `static_assert()` would require a statement-expression
236  *
237  * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
238  *    are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
239  *    the purpose of enabling statement-expressions in this one instance.
240  *
241  * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
242  *    however it's implemented in terms of `_Generic()`, which is not available
243  *    in C++.
244  *
245  * Combined this means we need separate solutions for C and C++.
246  *
247  * For C, as we don't have statement-expressions, we need to exploit some other
248  * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
249  * API function call. We also have to take care of the fact that the call-sites
250  * may be in the context of a variable assignment for error-handling purposes.
251  * The key observation is that we can use the comma operator as a sequence point
252  * to order the type check before the API call, discarding the "result" value of
253  * the type check and yielding the return value of the API call.
254  *
255  * C++ could be less of a headache than the C as we can leverage template
256  * functions. An advantage of template functions is that while their definition
257  * is driven by instantion, the definition does not appear at the source
258  * location of the instantation, which gives it a great leg-up over the problems
259  * we have in the C path. However, the use of the msgbuf APIs in the test suite
260  * still makes things somewhat tricky, as the call-sites in the test suite are
261  * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
262  * takes both the object type and the required type as template arguments, and
263  * then define the object pointer parameter as `void *` for a call through to
264  * the appropriate msgbuf API. However, because the msgbuf API call-sites are
265  * encapsulated in gtest macros, use of commas in the template specification
266  * causes pre-processor confusion. In this way we're constrained to only one
267  * template argument per function.
268  *
269  * Implement the C++ path using template functions that take the destination
270  * object type as a template argument, while the name of the function symbols
271  * are derived from the required type. The manual implementations of these
272  * appear at the end of the header. The type safety is actually enforced
273  * by `static_assert()` this time, as we can use statements as we're not
274  * constrained to an expression in the templated function body.
275  *
276  * The invocations of pldm_msgbuf_extract_typecheck() typically result in
277  * double-evaluation of some arguments. We're not yet bothered by this for two
278  * reasons:
279  *
280  * 1. The nature of the current call-sites are such that there are no
281  *    argument expressions that result in undesirable side-effects
282  *
283  * 2. It's an API internal to the libpldm implementation, and we can fix things
284  *    whenever something crops up the violates the observation in 1.
285  */
286 #ifdef __cplusplus
287 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
288 	pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
289 #else
290 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
291 	(pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
292 #endif
293 
294 /**
295  * @brief pldm_msgbuf extractor for a uint8_t
296  *
297  * @param[inout] ctx - pldm_msgbuf context for extractor
298  * @param[out] dst - destination of extracted value
299  *
300  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
301  * PLDM_ERROR_INVALID_LENGTH otherwise.
302  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
303  */
304 #define pldm_msgbuf_extract_uint8(ctx, dst)                                    \
305 	pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8,     \
306 				      dst, ctx, dst)
307 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
308 static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
309 {
310 	if (!ctx || !ctx->cursor || !dst) {
311 		return PLDM_ERROR_INVALID_DATA;
312 	}
313 
314 	if (ctx->remaining == INTMAX_MIN) {
315 		assert(ctx->remaining < 0);
316 		return PLDM_ERROR_INVALID_LENGTH;
317 	}
318 	ctx->remaining -= sizeof(uint8_t);
319 	assert(ctx->remaining >= 0);
320 	if (ctx->remaining < 0) {
321 		return PLDM_ERROR_INVALID_LENGTH;
322 	}
323 
324 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
325 
326 	ctx->cursor++;
327 	return PLDM_SUCCESS;
328 }
329 
330 #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
331 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
332 				      ctx, dst)
333 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
334 static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
335 {
336 	if (!ctx || !ctx->cursor || !dst) {
337 		return PLDM_ERROR_INVALID_DATA;
338 	}
339 
340 	if (ctx->remaining == INTMAX_MIN) {
341 		assert(ctx->remaining < 0);
342 		return PLDM_ERROR_INVALID_LENGTH;
343 	}
344 	ctx->remaining -= sizeof(int8_t);
345 	assert(ctx->remaining >= 0);
346 	if (ctx->remaining < 0) {
347 		return PLDM_ERROR_INVALID_LENGTH;
348 	}
349 
350 	memcpy(dst, ctx->cursor, sizeof(int8_t));
351 	ctx->cursor++;
352 	return PLDM_SUCCESS;
353 }
354 
355 #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
356 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
357 				      dst, ctx, dst)
358 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
359 static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
360 					      void *dst)
361 {
362 	uint16_t ldst;
363 
364 	if (!ctx || !ctx->cursor || !dst) {
365 		return PLDM_ERROR_INVALID_DATA;
366 	}
367 
368 	// Check for underflow while tracking the magnitude of the buffer overflow
369 	static_assert(
370 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
371 		sizeof(ldst) < INTMAX_MAX,
372 		"The following addition may not uphold the runtime assertion");
373 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
374 		assert(ctx->remaining < 0);
375 		return PLDM_ERROR_INVALID_LENGTH;
376 	}
377 
378 	// Check for buffer overflow. If we overflow, account for the request as
379 	// negative values in ctx->remaining. This way we can debug how far
380 	// we've overflowed.
381 	ctx->remaining -= sizeof(ldst);
382 
383 	// Prevent the access if it would overflow. First, assert so we blow up
384 	// the test suite right at the point of failure. However, cater to
385 	// -DNDEBUG by explicitly testing that the access is valid.
386 	assert(ctx->remaining >= 0);
387 	if (ctx->remaining < 0) {
388 		return PLDM_ERROR_INVALID_LENGTH;
389 	}
390 
391 	// Use memcpy() to have the compiler deal with any alignment
392 	// issues on the target architecture
393 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
394 
395 	// Only assign the target value once it's correctly decoded
396 	ldst = le16toh(ldst);
397 
398 	// Allow storing to unaligned
399 	memcpy(dst, &ldst, sizeof(ldst));
400 
401 	ctx->cursor += sizeof(ldst);
402 
403 	return PLDM_SUCCESS;
404 }
405 
406 #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
407 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
408 				      dst, ctx, dst)
409 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
410 static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
411 {
412 	int16_t ldst;
413 
414 	if (!ctx || !ctx->cursor || !dst) {
415 		return PLDM_ERROR_INVALID_DATA;
416 	}
417 
418 	static_assert(
419 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
420 		sizeof(ldst) < INTMAX_MAX,
421 		"The following addition may not uphold the runtime assertion");
422 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
423 		assert(ctx->remaining < 0);
424 		return PLDM_ERROR_INVALID_LENGTH;
425 	}
426 	ctx->remaining -= sizeof(ldst);
427 	assert(ctx->remaining >= 0);
428 	if (ctx->remaining < 0) {
429 		return PLDM_ERROR_INVALID_LENGTH;
430 	}
431 
432 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
433 
434 	ldst = le16toh(ldst);
435 	memcpy(dst, &ldst, sizeof(ldst));
436 	ctx->cursor += sizeof(ldst);
437 
438 	return PLDM_SUCCESS;
439 }
440 
441 #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
442 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
443 				      dst, ctx, dst)
444 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
445 static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
446 					      void *dst)
447 {
448 	uint32_t ldst;
449 
450 	if (!ctx || !ctx->cursor || !dst) {
451 		return PLDM_ERROR_INVALID_DATA;
452 	}
453 
454 	static_assert(
455 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
456 		sizeof(ldst) < INTMAX_MAX,
457 		"The following addition may not uphold the runtime assertion");
458 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
459 		assert(ctx->remaining < 0);
460 		return PLDM_ERROR_INVALID_LENGTH;
461 	}
462 	ctx->remaining -= sizeof(ldst);
463 	assert(ctx->remaining >= 0);
464 	if (ctx->remaining < 0) {
465 		return PLDM_ERROR_INVALID_LENGTH;
466 	}
467 
468 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
469 
470 	ldst = le32toh(ldst);
471 	memcpy(dst, &ldst, sizeof(ldst));
472 	ctx->cursor += sizeof(ldst);
473 
474 	return PLDM_SUCCESS;
475 }
476 
477 #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
478 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
479 				      dst, ctx, dst)
480 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
481 static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
482 {
483 	int32_t ldst;
484 
485 	if (!ctx || !ctx->cursor || !dst) {
486 		return PLDM_ERROR_INVALID_DATA;
487 	}
488 
489 	static_assert(
490 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
491 		sizeof(ldst) < INTMAX_MAX,
492 		"The following addition may not uphold the runtime assertion");
493 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
494 		assert(ctx->remaining < 0);
495 		return PLDM_ERROR_INVALID_LENGTH;
496 	}
497 	ctx->remaining -= sizeof(ldst);
498 	assert(ctx->remaining >= 0);
499 	if (ctx->remaining < 0) {
500 		return PLDM_ERROR_INVALID_LENGTH;
501 	}
502 
503 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
504 
505 	ldst = le32toh(ldst);
506 	memcpy(dst, &ldst, sizeof(ldst));
507 	ctx->cursor += sizeof(ldst);
508 
509 	return PLDM_SUCCESS;
510 }
511 
512 #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
513 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
514 				      dst, ctx, dst)
515 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
516 static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx,
517 					      void *dst)
518 {
519 	uint32_t ldst;
520 
521 	_Static_assert(sizeof(real32_t) == sizeof(ldst),
522 		       "Mismatched type sizes for dst and ldst");
523 
524 	if (!ctx || !ctx->cursor || !dst) {
525 		return PLDM_ERROR_INVALID_DATA;
526 	}
527 
528 	static_assert(
529 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
530 		sizeof(ldst) < INTMAX_MAX,
531 		"The following addition may not uphold the runtime assertion");
532 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
533 		assert(ctx->remaining < 0);
534 		return PLDM_ERROR_INVALID_LENGTH;
535 	}
536 	ctx->remaining -= sizeof(ldst);
537 	assert(ctx->remaining >= 0);
538 	if (ctx->remaining < 0) {
539 		return PLDM_ERROR_INVALID_LENGTH;
540 	}
541 
542 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
543 	ldst = le32toh(ldst);
544 	memcpy(dst, &ldst, sizeof(ldst));
545 	ctx->cursor += sizeof(ldst);
546 
547 	return PLDM_SUCCESS;
548 }
549 
550 /**
551  * Extract the field at the msgbuf cursor into the lvalue named by dst.
552  *
553  * @param ctx The msgbuf context object
554  * @param dst The lvalue into which the field at the msgbuf cursor should be
555  *            extracted
556  *
557  * @return PLDM_SUCCESS on success, otherwise another value on error
558  */
559 #define pldm_msgbuf_extract(ctx, dst)                                          \
560 	_Generic((dst),                                                        \
561 		uint8_t: pldm__msgbuf_extract_uint8,                           \
562 		int8_t: pldm__msgbuf_extract_int8,                             \
563 		uint16_t: pldm__msgbuf_extract_uint16,                         \
564 		int16_t: pldm__msgbuf_extract_int16,                           \
565 		uint32_t: pldm__msgbuf_extract_uint32,                         \
566 		int32_t: pldm__msgbuf_extract_int32,                           \
567 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
568 
569 /**
570  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
571  *
572  * @param ctx The msgbuf context object
573  * @param dst The pointer to the object into which the field at the msgbuf
574  *            cursor should be extracted
575  *
576  * @return PLDM_SUCCESS on success, otherwise another value on error
577  */
578 #define pldm_msgbuf_extract_p(ctx, dst)                                        \
579 	_Generic((dst),                                                        \
580 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
581 		int8_t *: pldm__msgbuf_extract_int8,                           \
582 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
583 		int16_t *: pldm__msgbuf_extract_int16,                         \
584 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
585 		int32_t *: pldm__msgbuf_extract_int32,                         \
586 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
587 
588 static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
589 						  uint8_t *dst, size_t count)
590 {
591 	if (!ctx || !ctx->cursor || !dst) {
592 		return PLDM_ERROR_INVALID_DATA;
593 	}
594 
595 	if (!count) {
596 		return PLDM_SUCCESS;
597 	}
598 
599 #if INTMAX_MAX < SIZE_MAX
600 	if (count > INTMAX_MAX) {
601 		return PLDM_ERROR_INVALID_LENGTH;
602 	}
603 #endif
604 
605 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
606 		return PLDM_ERROR_INVALID_LENGTH;
607 	}
608 	ctx->remaining -= (intmax_t)count;
609 	assert(ctx->remaining >= 0);
610 	if (ctx->remaining < 0) {
611 		return PLDM_ERROR_INVALID_LENGTH;
612 	}
613 
614 	memcpy(dst, ctx->cursor, count);
615 	ctx->cursor += count;
616 
617 	return PLDM_SUCCESS;
618 }
619 
620 #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
621 	_Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
622 								     count)
623 
624 static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
625 					    const uint32_t src)
626 {
627 	uint32_t val = htole32(src);
628 
629 	if (!ctx || !ctx->cursor) {
630 		return PLDM_ERROR_INVALID_DATA;
631 	}
632 
633 	static_assert(
634 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
635 		sizeof(src) < INTMAX_MAX,
636 		"The following addition may not uphold the runtime assertion");
637 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
638 		assert(ctx->remaining < 0);
639 		return PLDM_ERROR_INVALID_LENGTH;
640 	}
641 	ctx->remaining -= sizeof(src);
642 	assert(ctx->remaining >= 0);
643 	if (ctx->remaining < 0) {
644 		return PLDM_ERROR_INVALID_LENGTH;
645 	}
646 
647 	memcpy(ctx->cursor, &val, sizeof(val));
648 	ctx->cursor += sizeof(src);
649 
650 	return PLDM_SUCCESS;
651 }
652 
653 static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
654 					    const uint16_t src)
655 {
656 	uint16_t val = htole16(src);
657 
658 	if (!ctx || !ctx->cursor) {
659 		return PLDM_ERROR_INVALID_DATA;
660 	}
661 
662 	static_assert(
663 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
664 		sizeof(src) < INTMAX_MAX,
665 		"The following addition may not uphold the runtime assertion");
666 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
667 		assert(ctx->remaining < 0);
668 		return PLDM_ERROR_INVALID_LENGTH;
669 	}
670 	ctx->remaining -= sizeof(src);
671 	assert(ctx->remaining >= 0);
672 	if (ctx->remaining < 0) {
673 		return PLDM_ERROR_INVALID_LENGTH;
674 	}
675 
676 	memcpy(ctx->cursor, &val, sizeof(val));
677 	ctx->cursor += sizeof(src);
678 
679 	return PLDM_SUCCESS;
680 }
681 
682 static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
683 					   const uint8_t src)
684 {
685 	if (!ctx || !ctx->cursor) {
686 		return PLDM_ERROR_INVALID_DATA;
687 	}
688 
689 	static_assert(
690 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
691 		sizeof(src) < INTMAX_MAX,
692 		"The following addition may not uphold the runtime assertion");
693 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
694 		assert(ctx->remaining < 0);
695 		return PLDM_ERROR_INVALID_LENGTH;
696 	}
697 	ctx->remaining -= sizeof(src);
698 	assert(ctx->remaining >= 0);
699 	if (ctx->remaining < 0) {
700 		return PLDM_ERROR_INVALID_LENGTH;
701 	}
702 
703 	memcpy(ctx->cursor, &src, sizeof(src));
704 	ctx->cursor += sizeof(src);
705 
706 	return PLDM_SUCCESS;
707 }
708 
709 static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
710 					   const int32_t src)
711 {
712 	int32_t val = htole32(src);
713 
714 	if (!ctx || !ctx->cursor) {
715 		return PLDM_ERROR_INVALID_DATA;
716 	}
717 
718 	static_assert(
719 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
720 		sizeof(src) < INTMAX_MAX,
721 		"The following addition may not uphold the runtime assertion");
722 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
723 		assert(ctx->remaining < 0);
724 		return PLDM_ERROR_INVALID_LENGTH;
725 	}
726 	ctx->remaining -= sizeof(src);
727 	assert(ctx->remaining >= 0);
728 	if (ctx->remaining < 0) {
729 		return PLDM_ERROR_INVALID_LENGTH;
730 	}
731 
732 	memcpy(ctx->cursor, &val, sizeof(val));
733 	ctx->cursor += sizeof(src);
734 
735 	return PLDM_SUCCESS;
736 }
737 
738 static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
739 					   const int16_t src)
740 {
741 	int16_t val = htole16(src);
742 
743 	if (!ctx || !ctx->cursor) {
744 		return PLDM_ERROR_INVALID_DATA;
745 	}
746 
747 	static_assert(
748 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
749 		sizeof(src) < INTMAX_MAX,
750 		"The following addition may not uphold the runtime assertion");
751 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
752 		assert(ctx->remaining < 0);
753 		return PLDM_ERROR_INVALID_LENGTH;
754 	}
755 	ctx->remaining -= sizeof(src);
756 	assert(ctx->remaining >= 0);
757 	if (ctx->remaining < 0) {
758 		return PLDM_ERROR_INVALID_LENGTH;
759 	}
760 
761 	memcpy(ctx->cursor, &val, sizeof(val));
762 	ctx->cursor += sizeof(src);
763 
764 	return PLDM_SUCCESS;
765 }
766 
767 static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
768 					  const int8_t src)
769 {
770 	if (!ctx || !ctx->cursor) {
771 		return PLDM_ERROR_INVALID_DATA;
772 	}
773 
774 	static_assert(
775 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
776 		sizeof(src) < INTMAX_MAX,
777 		"The following addition may not uphold the runtime assertion");
778 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
779 		assert(ctx->remaining < 0);
780 		return PLDM_ERROR_INVALID_LENGTH;
781 	}
782 	ctx->remaining -= sizeof(src);
783 	assert(ctx->remaining >= 0);
784 	if (ctx->remaining < 0) {
785 		return PLDM_ERROR_INVALID_LENGTH;
786 	}
787 
788 	memcpy(ctx->cursor, &src, sizeof(src));
789 	ctx->cursor += sizeof(src);
790 
791 	return PLDM_SUCCESS;
792 }
793 
794 #define pldm_msgbuf_insert(dst, src)                                           \
795 	_Generic((src),                                                        \
796 		uint8_t: pldm_msgbuf_insert_uint8,                             \
797 		int8_t: pldm_msgbuf_insert_int8,                               \
798 		uint16_t: pldm_msgbuf_insert_uint16,                           \
799 		int16_t: pldm_msgbuf_insert_int16,                             \
800 		uint32_t: pldm_msgbuf_insert_uint32,                           \
801 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
802 
803 static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
804 						 const uint8_t *src,
805 						 size_t count)
806 {
807 	if (!ctx || !ctx->cursor || !src) {
808 		return PLDM_ERROR_INVALID_DATA;
809 	}
810 
811 	if (!count) {
812 		return PLDM_SUCCESS;
813 	}
814 
815 #if INTMAX_MAX < SIZE_MAX
816 	if (count > INTMAX_MAX) {
817 		return PLDM_ERROR_INVALID_LENGTH;
818 	}
819 #endif
820 
821 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
822 		return PLDM_ERROR_INVALID_LENGTH;
823 	}
824 	ctx->remaining -= (intmax_t)count;
825 	assert(ctx->remaining >= 0);
826 	if (ctx->remaining < 0) {
827 		return PLDM_ERROR_INVALID_LENGTH;
828 	}
829 
830 	memcpy(ctx->cursor, src, count);
831 	ctx->cursor += count;
832 
833 	return PLDM_SUCCESS;
834 }
835 
836 #define pldm_msgbuf_insert_array(dst, src, count)                              \
837 	_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src,  \
838 								    count)
839 
840 static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
841 					    size_t required, void **cursor)
842 {
843 	if (!ctx || !ctx->cursor || !cursor || *cursor) {
844 		return PLDM_ERROR_INVALID_DATA;
845 	}
846 
847 #if INTMAX_MAX < SIZE_MAX
848 	if (required > INTMAX_MAX) {
849 		return PLDM_ERROR_INVALID_LENGTH;
850 	}
851 #endif
852 
853 	if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
854 		return PLDM_ERROR_INVALID_LENGTH;
855 	}
856 	ctx->remaining -= (intmax_t)required;
857 	assert(ctx->remaining >= 0);
858 	if (ctx->remaining < 0) {
859 		return PLDM_ERROR_INVALID_LENGTH;
860 	}
861 
862 	*cursor = ctx->cursor;
863 	ctx->cursor += required;
864 
865 	return PLDM_SUCCESS;
866 }
867 
868 static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
869 					     void **cursor, size_t *len)
870 {
871 	if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
872 		return PLDM_ERROR_INVALID_DATA;
873 	}
874 
875 	assert(ctx->remaining >= 0);
876 	if (ctx->remaining < 0) {
877 		return PLDM_ERROR_INVALID_LENGTH;
878 	}
879 
880 	*cursor = ctx->cursor;
881 	ctx->cursor += ctx->remaining;
882 	*len = ctx->remaining;
883 	ctx->remaining = 0;
884 
885 	return PLDM_SUCCESS;
886 }
887 #ifdef __cplusplus
888 }
889 #endif
890 
891 #ifdef __cplusplus
892 #include <type_traits>
893 
894 template <typename T>
895 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
896 						void *buf)
897 {
898 	static_assert(std::is_same<uint8_t *, T>::value);
899 	return pldm__msgbuf_extract_uint8(ctx, buf);
900 }
901 
902 template <typename T>
903 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
904 					       void *buf)
905 {
906 	static_assert(std::is_same<int8_t *, T>::value);
907 	return pldm__msgbuf_extract_int8(ctx, buf);
908 }
909 
910 template <typename T>
911 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
912 						 void *buf)
913 {
914 	static_assert(std::is_same<uint16_t *, T>::value);
915 	return pldm__msgbuf_extract_uint16(ctx, buf);
916 }
917 
918 template <typename T>
919 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
920 						void *buf)
921 {
922 	static_assert(std::is_same<int16_t *, T>::value);
923 	return pldm__msgbuf_extract_int16(ctx, buf);
924 }
925 
926 template <typename T>
927 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
928 						 void *buf)
929 {
930 	static_assert(std::is_same<uint32_t *, T>::value);
931 	return pldm__msgbuf_extract_uint32(ctx, buf);
932 }
933 
934 template <typename T>
935 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
936 						void *buf)
937 {
938 	static_assert(std::is_same<int32_t *, T>::value);
939 	return pldm__msgbuf_extract_int32(ctx, buf);
940 }
941 
942 template <typename T>
943 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
944 						 void *buf)
945 {
946 	static_assert(std::is_same<real32_t *, T>::value);
947 	return pldm__msgbuf_extract_real32(ctx, buf);
948 }
949 #endif
950 
951 #endif /* BUF_H */
952