xref: /openbmc/libpldm/src/msgbuf.h (revision 860a43d9)
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 #include "compiler.h"
6 
7 /*
8  * Historically, many of the structs exposed in libpldm's public headers are
9  * defined with __attribute__((packed)). This is unfortunate: it gives the
10  * impression that a wire-format buffer can be cast to the message type to make
11  * the message's fields easily accessible. As it turns out, that's not
12  * that's valid for several reasons:
13  *
14  * 1. Casting the wire-format buffer to a struct of the message type doesn't
15  *    abstract the endianness of message field values
16  *
17  * 2. Some messages contain packed tagged union fields which cannot be properly
18  *    described in a C struct.
19  *
20  * The msgbuf APIs exist to assist with (un)packing the wire-format in a way
21  * that is type-safe, spatially memory-safe, endian-safe, performant, and
22  * free of undefined-behaviour. Message structs that are added to the public
23  * library API should no-longer be marked __attribute__((packed)), and the
24  * implementation of their encode and decode functions must exploit the msgbuf
25  * API.
26  *
27  * However, we would like to allow implementation of codec functions in terms of
28  * msgbuf APIs even if they're decoding a message into a (historically) packed
29  * struct. Some of the complexity that follows is a consequence of the packed/
30  * unpacked conflict.
31  */
32 
33 #ifdef __cplusplus
34 /*
35  * Fix up C11's _Static_assert() vs C++'s static_assert().
36  *
37  * Can we please have nice things for once.
38  */
39 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
40 #define _Static_assert(...) static_assert(__VA_ARGS__)
41 extern "C" {
42 #endif
43 
44 #include <libpldm/base.h>
45 #include <libpldm/pldm_types.h>
46 
47 #include "compiler.h"
48 
49 #include <assert.h>
50 #include <endian.h>
51 #include <errno.h>
52 #include <limits.h>
53 #include <stdbool.h>
54 #include <stdint.h>
55 #include <string.h>
56 #include <sys/types.h>
57 #include <uchar.h>
58 
59 /*
60  * We can't use static_assert() outside of some other C construct. Deal
61  * with high-level global assertions by burying them in an unused struct
62  * declaration, that has a sole member for compliance with the requirement that
63  * types must have a size.
64 */
65 static struct {
66 	static_assert(
67 		INTMAX_MAX != SIZE_MAX,
68 		"Extraction and insertion value comparisons may be broken");
69 	static_assert(INTMAX_MIN + INTMAX_MAX <= 0,
70 		      "Extraction and insertion arithmetic may be broken");
71 	static_assert(PLDM_SUCCESS == 0, "Error handling is broken");
72 	int compliance;
73 } build_assertions LIBPLDM_CC_UNUSED;
74 
75 enum pldm_msgbuf_error_mode {
76 	PLDM_MSGBUF_PLDM_CC = 0x5a,
77 	PLDM_MSGBUF_C_ERRNO = 0xa5,
78 };
79 
80 struct pldm_msgbuf {
81 	uint8_t *cursor;
82 	intmax_t remaining;
83 	enum pldm_msgbuf_error_mode mode;
84 };
85 
86 /**
87  * @brief Either negate an errno value or return a value mapped to a PLDM
88  * completion code.
89  *
90  * Note that `pldm_msgbuf_status()` is purely internal to the msgbuf API
91  * for ergonomics. It's preferred that we don't try to unify this with
92  * `pldm_xlate_errno()` from src/api.h despite the similarities.
93  *
94  * @param[in] ctx - The msgbuf context providing the personality info
95  * @param[in] err - The positive errno value to translate
96  *
97  * @return Either the negated value of @p err if the context's error mode is
98  *         `PLDM_MSGBUF_C_ERRNO`, or the equivalent PLDM completion code if the
99  *         error mode is `PLDM_MSGBUF_PLDM_CC`.
100  */
101 __attribute__((always_inline)) static inline int
102 pldm_msgbuf_status(struct pldm_msgbuf *ctx, unsigned int err)
103 {
104 	int rc;
105 
106 	assert(err != 0);
107 	assert(err <= INT_MAX);
108 
109 	if (ctx->mode == PLDM_MSGBUF_C_ERRNO) {
110 		if (err > INT_MAX) {
111 			return -EINVAL;
112 		}
113 
114 		static_assert(INT_MIN + INT_MAX < 0,
115 			      "Arithmetic assumption failure");
116 		return -((int)err);
117 	}
118 
119 	if (err > INT_MAX) {
120 		return PLDM_ERROR;
121 	}
122 
123 	assert(ctx->mode == PLDM_MSGBUF_PLDM_CC);
124 	switch (err) {
125 	case EINVAL:
126 		rc = PLDM_ERROR_INVALID_DATA;
127 		break;
128 	case EBADMSG:
129 	case EOVERFLOW:
130 		rc = PLDM_ERROR_INVALID_LENGTH;
131 		break;
132 	default:
133 		assert(false);
134 		rc = PLDM_ERROR;
135 		break;
136 	}
137 
138 	assert(rc > 0);
139 	return rc;
140 }
141 
142 /**
143  * @brief Initialize pldm buf struct for buf extractor
144  *
145  * @param[out] ctx - pldm_msgbuf context for extractor
146  * @param[in] minsize - The minimum required length of buffer `buf`
147  * @param[in] buf - buffer to be extracted
148  * @param[in] len - size of buffer
149  *
150  * @return 0 on success, otherwise an error code appropriate for the current
151  *         personality.
152  */
153 __attribute__((always_inline)) static inline int
154 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
155 pldm__msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
156 		  size_t len)
157 {
158 	assert(ctx);
159 	assert(ctx->mode == PLDM_MSGBUF_PLDM_CC ||
160 	       ctx->mode == PLDM_MSGBUF_C_ERRNO);
161 
162 	if (!buf) {
163 		return pldm_msgbuf_status(ctx, EINVAL);
164 	}
165 
166 	if ((minsize > len)) {
167 		return pldm_msgbuf_status(ctx, EOVERFLOW);
168 	}
169 
170 #if INTMAX_MAX < SIZE_MAX
171 	if (len > INTMAX_MAX) {
172 		return pldm_msgbuf_status(ctx, EOVERFLOW);
173 	}
174 #endif
175 
176 	if ((uintptr_t)buf + len < len) {
177 		return pldm_msgbuf_status(ctx, EOVERFLOW);
178 	}
179 
180 	ctx->cursor = (uint8_t *)buf;
181 	ctx->remaining = (intmax_t)len;
182 
183 	return 0;
184 }
185 
186 /**
187  * @brief Initialise a msgbuf instance to return errors as PLDM completion codes
188  *
189  * @see pldm__msgbuf_init
190  *
191  * @param[out] ctx - pldm_msgbuf context for extractor
192  * @param[in] minsize - The minimum required length of buffer `buf`
193  * @param[in] buf - buffer to be extracted
194  * @param[in] len - size of buffer
195  *
196  * @return PLDM_SUCCESS if the provided buffer region is sensible,
197  *         otherwise PLDM_ERROR_INVALID_DATA if pointer parameters are invalid,
198  *         or PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
199  */
200 __attribute__((always_inline)) static inline int
201 pldm_msgbuf_init_cc(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
202 		    size_t len)
203 {
204 	if (!ctx) {
205 		return PLDM_ERROR_INVALID_DATA;
206 	}
207 
208 	ctx->mode = PLDM_MSGBUF_PLDM_CC;
209 	return pldm__msgbuf_init(ctx, minsize, buf, len);
210 }
211 
212 /**
213  * @brief Initialise a msgbuf instance to return errors as negative errno values
214  *
215  * @see pldm__msgbuf_init
216  *
217  * @param[out] ctx - pldm_msgbuf context for extractor
218  * @param[in] minsize - The minimum required length of buffer `buf`
219  * @param[in] buf - buffer to be extracted
220  * @param[in] len - size of buffer
221  *
222  * @return 0 if the provided buffer region is sensible, otherwise -EINVAL if
223  *         pointer parameters are invalid, or -EOVERFLOW if length constraints
224  *         are violated.
225  */
226 __attribute__((always_inline)) static inline int
227 pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
228 		       size_t len)
229 {
230 	if (!ctx) {
231 		return -EINVAL;
232 	}
233 
234 	ctx->mode = PLDM_MSGBUF_C_ERRNO;
235 	return pldm__msgbuf_init(ctx, minsize, buf, len);
236 }
237 
238 /**
239  * @brief Validate buffer overflow state
240  *
241  * @param[in] ctx - pldm_msgbuf context for extractor
242  *
243  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
244  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
245  * prior accesses would have occurred beyond the bounds of the buffer, and
246  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
247  * pointer.
248  */
249 __attribute__((always_inline)) static inline int
250 pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
251 {
252 	assert(ctx);
253 	if (ctx->remaining < 0) {
254 		return pldm_msgbuf_status(ctx, EOVERFLOW);
255 	}
256 
257 	return 0;
258 }
259 
260 /**
261  * @brief Test whether a message buffer has been exactly consumed
262  *
263  * @param[in] ctx - pldm_msgbuf context for extractor
264  *
265  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
266  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
267  * indicates that an incorrect sequence of accesses have occurred, and
268  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
269  * pointer.
270  */
271 __attribute__((always_inline)) static inline int
272 pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
273 {
274 	assert(ctx);
275 	if (ctx->remaining != 0) {
276 		return pldm_msgbuf_status(ctx, EBADMSG);
277 	}
278 
279 	return 0;
280 }
281 
282 /**
283  * @brief Destroy the pldm buf
284  *
285  * @param[in] ctx - pldm_msgbuf context for extractor
286  *
287  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
288  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
289  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
290  * bounds of the buffer.
291  */
292 __attribute__((always_inline)) static inline int
293 pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
294 {
295 	int valid;
296 
297 	assert(ctx);
298 	valid = pldm_msgbuf_validate(ctx);
299 
300 	ctx->cursor = NULL;
301 	ctx->remaining = 0;
302 
303 	return valid;
304 }
305 
306 /**
307  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
308  * has been completely consumed without overflow
309  *
310  * @param[in] ctx - pldm_msgbuf context
311  *
312  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
313  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
314  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
315  * have occurred byond the bounds of the buffer
316  */
317 __attribute__((always_inline)) static inline int
318 pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
319 {
320 	int consumed;
321 
322 	assert(ctx);
323 	consumed = pldm_msgbuf_consumed(ctx);
324 
325 	ctx->cursor = NULL;
326 	ctx->remaining = 0;
327 
328 	return consumed;
329 }
330 
331 /*
332  * Exploit the pre-processor to perform type checking by macro substitution.
333  *
334  * A C type is defined by its alignment as well as its object
335  * size, and compilers have a hammer to enforce it in the form of
336  * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
337  * the libpldm public API this presents a problem: Naively attempting to use the
338  * msgbuf APIs on a member of a packed struct would yield an error.
339  *
340  * The msgbuf APIs are implemented such that data is moved through unaligned
341  * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
342  * make the object pointers take a trip through `void *` at its API boundary.
343  * That presents a bit too much of an opportunity to non-surgically remove your
344  * own foot, so here we set about doing something to mitigate that as well.
345  *
346  * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
347  * only for the purpose of object sizes, disregarding alignment. We have a few
348  * constraints that cause some headaches:
349  *
350  * 1. We have to perform the type-check before a call through a C function,
351  *    as the function must take the object pointer argument as `void *`.
352  *    Essentially, this constrains us to doing something with macros.
353  *
354  * 2. While libpldm is a C library, its test suite is written in C++ to take
355  *    advantage of gtest.
356  *
357  * 3. Ideally we'd do something with C's `static_assert()`, however
358  *    `static_assert()` is defined as void, and as we're constrained to macros,
359  *    using `static_assert()` would require a statement-expression
360  *
361  * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
362  *    are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
363  *    the purpose of enabling statement-expressions in this one instance.
364  *
365  * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
366  *    however it's implemented in terms of `_Generic()`, which is not available
367  *    in C++.
368  *
369  * Combined this means we need separate solutions for C and C++.
370  *
371  * For C, as we don't have statement-expressions, we need to exploit some other
372  * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
373  * API function call. We also have to take care of the fact that the call-sites
374  * may be in the context of a variable assignment for error-handling purposes.
375  * The key observation is that we can use the comma operator as a sequence point
376  * to order the type check before the API call, discarding the "result" value of
377  * the type check and yielding the return value of the API call.
378  *
379  * C++ could be less of a headache than the C as we can leverage template
380  * functions. An advantage of template functions is that while their definition
381  * is driven by instantion, the definition does not appear at the source
382  * location of the instantiation, which gives it a great leg-up over the problems
383  * we have in the C path. However, the use of the msgbuf APIs in the test suite
384  * still makes things somewhat tricky, as the call-sites in the test suite are
385  * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
386  * takes both the object type and the required type as template arguments, and
387  * then define the object pointer parameter as `void *` for a call through to
388  * the appropriate msgbuf API. However, because the msgbuf API call-sites are
389  * encapsulated in gtest macros, use of commas in the template specification
390  * causes pre-processor confusion. In this way we're constrained to only one
391  * template argument per function.
392  *
393  * Implement the C++ path using template functions that take the destination
394  * object type as a template argument, while the name of the function symbols
395  * are derived from the required type. The manual implementations of these
396  * appear at the end of the header. The type safety is actually enforced
397  * by `static_assert()` this time, as we can use statements as we're not
398  * constrained to an expression in the templated function body.
399  *
400  * The invocations of pldm_msgbuf_extract_typecheck() typically result in
401  * double-evaluation of some arguments. We're not yet bothered by this for two
402  * reasons:
403  *
404  * 1. The nature of the current call-sites are such that there are no
405  *    argument expressions that result in undesirable side-effects
406  *
407  * 2. It's an API internal to the libpldm implementation, and we can fix things
408  *    whenever something crops up the violates the observation in 1.
409  */
410 #ifdef __cplusplus
411 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
412 	pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
413 #else
414 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
415 	(pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
416 #endif
417 
418 /**
419  * @brief pldm_msgbuf extractor for a uint8_t
420  *
421  * @param[in,out] ctx - pldm_msgbuf context for extractor
422  * @param[out] dst - destination of extracted value
423  *
424  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
425  * PLDM_ERROR_INVALID_LENGTH otherwise.
426  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
427  */
428 #define pldm_msgbuf_extract_uint8(ctx, dst)                                    \
429 	pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8,     \
430 				      dst, ctx, dst)
431 __attribute__((always_inline)) static inline int
432 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
433 pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
434 {
435 	assert(ctx);
436 
437 	if (!ctx->cursor || !dst) {
438 		return pldm_msgbuf_status(ctx, EINVAL);
439 	}
440 
441 	if (ctx->remaining == INTMAX_MIN) {
442 		assert(ctx->remaining < 0);
443 		return pldm_msgbuf_status(ctx, EOVERFLOW);
444 	}
445 	ctx->remaining -= sizeof(uint8_t);
446 	assert(ctx->remaining >= 0);
447 	if (ctx->remaining < 0) {
448 		return pldm_msgbuf_status(ctx, EOVERFLOW);
449 	}
450 
451 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
452 
453 	ctx->cursor++;
454 	return 0;
455 }
456 
457 #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
458 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
459 				      ctx, dst)
460 __attribute__((always_inline)) static inline int
461 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
462 pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
463 {
464 	assert(ctx);
465 
466 	if (!ctx->cursor || !dst) {
467 		return pldm_msgbuf_status(ctx, EINVAL);
468 	}
469 
470 	if (ctx->remaining == INTMAX_MIN) {
471 		assert(ctx->remaining < 0);
472 		return pldm_msgbuf_status(ctx, EOVERFLOW);
473 	}
474 	ctx->remaining -= sizeof(int8_t);
475 	assert(ctx->remaining >= 0);
476 	if (ctx->remaining < 0) {
477 		return pldm_msgbuf_status(ctx, EOVERFLOW);
478 	}
479 
480 	memcpy(dst, ctx->cursor, sizeof(int8_t));
481 	ctx->cursor++;
482 	return 0;
483 }
484 
485 #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
486 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
487 				      dst, ctx, dst)
488 __attribute__((always_inline)) static inline int
489 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
490 pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst)
491 {
492 	uint16_t ldst;
493 
494 	assert(ctx);
495 
496 	if (!ctx->cursor || !dst) {
497 		return pldm_msgbuf_status(ctx, EINVAL);
498 	}
499 
500 	// Check for underflow while tracking the magnitude of the buffer overflow
501 	static_assert(
502 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
503 		sizeof(ldst) < INTMAX_MAX,
504 		"The following addition may not uphold the runtime assertion");
505 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
506 		assert(ctx->remaining < 0);
507 		return pldm_msgbuf_status(ctx, EOVERFLOW);
508 	}
509 
510 	// Check for buffer overflow. If we overflow, account for the request as
511 	// negative values in ctx->remaining. This way we can debug how far
512 	// we've overflowed.
513 	ctx->remaining -= sizeof(ldst);
514 
515 	// Prevent the access if it would overflow. First, assert so we blow up
516 	// the test suite right at the point of failure. However, cater to
517 	// -DNDEBUG by explicitly testing that the access is valid.
518 	assert(ctx->remaining >= 0);
519 	if (ctx->remaining < 0) {
520 		return pldm_msgbuf_status(ctx, EOVERFLOW);
521 	}
522 
523 	// Use memcpy() to have the compiler deal with any alignment
524 	// issues on the target architecture
525 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
526 
527 	// Only assign the target value once it's correctly decoded
528 	ldst = le16toh(ldst);
529 
530 	// Allow storing to unaligned
531 	memcpy(dst, &ldst, sizeof(ldst));
532 
533 	ctx->cursor += sizeof(ldst);
534 
535 	return 0;
536 }
537 
538 #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
539 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
540 				      dst, ctx, dst)
541 __attribute__((always_inline)) static inline int
542 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
543 pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
544 {
545 	int16_t ldst;
546 
547 	assert(ctx);
548 
549 	if (!ctx->cursor || !dst) {
550 		return pldm_msgbuf_status(ctx, EINVAL);
551 	}
552 
553 	static_assert(
554 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
555 		sizeof(ldst) < INTMAX_MAX,
556 		"The following addition may not uphold the runtime assertion");
557 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
558 		assert(ctx->remaining < 0);
559 		return pldm_msgbuf_status(ctx, EOVERFLOW);
560 	}
561 	ctx->remaining -= sizeof(ldst);
562 	assert(ctx->remaining >= 0);
563 	if (ctx->remaining < 0) {
564 		return pldm_msgbuf_status(ctx, EOVERFLOW);
565 	}
566 
567 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
568 
569 	ldst = le16toh(ldst);
570 	memcpy(dst, &ldst, sizeof(ldst));
571 	ctx->cursor += sizeof(ldst);
572 
573 	return 0;
574 }
575 
576 #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
577 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
578 				      dst, ctx, dst)
579 __attribute__((always_inline)) static inline int
580 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
581 pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst)
582 {
583 	uint32_t ldst;
584 
585 	assert(ctx);
586 
587 	if (!ctx->cursor || !dst) {
588 		return pldm_msgbuf_status(ctx, EINVAL);
589 	}
590 
591 	static_assert(
592 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
593 		sizeof(ldst) < INTMAX_MAX,
594 		"The following addition may not uphold the runtime assertion");
595 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
596 		assert(ctx->remaining < 0);
597 		return pldm_msgbuf_status(ctx, EOVERFLOW);
598 	}
599 	ctx->remaining -= sizeof(ldst);
600 	assert(ctx->remaining >= 0);
601 	if (ctx->remaining < 0) {
602 		return pldm_msgbuf_status(ctx, EOVERFLOW);
603 	}
604 
605 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
606 	ldst = le32toh(ldst);
607 	memcpy(dst, &ldst, sizeof(ldst));
608 	ctx->cursor += sizeof(ldst);
609 
610 	return 0;
611 }
612 
613 #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
614 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
615 				      dst, ctx, dst)
616 __attribute__((always_inline)) static inline int
617 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
618 pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
619 {
620 	int32_t ldst;
621 
622 	assert(ctx);
623 
624 	if (!ctx->cursor || !dst) {
625 		return pldm_msgbuf_status(ctx, EINVAL);
626 	}
627 
628 	static_assert(
629 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
630 		sizeof(ldst) < INTMAX_MAX,
631 		"The following addition may not uphold the runtime assertion");
632 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
633 		assert(ctx->remaining < 0);
634 		return pldm_msgbuf_status(ctx, EOVERFLOW);
635 	}
636 	ctx->remaining -= sizeof(ldst);
637 	assert(ctx->remaining >= 0);
638 	if (ctx->remaining < 0) {
639 		return pldm_msgbuf_status(ctx, EOVERFLOW);
640 	}
641 
642 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
643 	ldst = le32toh(ldst);
644 	memcpy(dst, &ldst, sizeof(ldst));
645 	ctx->cursor += sizeof(ldst);
646 
647 	return PLDM_SUCCESS;
648 }
649 
650 #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
651 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
652 				      dst, ctx, dst)
653 __attribute__((always_inline)) static inline int
654 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
655 pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst)
656 {
657 	uint32_t ldst;
658 
659 	static_assert(sizeof(real32_t) == sizeof(ldst),
660 		      "Mismatched type sizes for dst and ldst");
661 
662 	assert(ctx);
663 
664 	if (!ctx->cursor || !dst) {
665 		return pldm_msgbuf_status(ctx, EINVAL);
666 	}
667 
668 	static_assert(
669 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
670 		sizeof(ldst) < INTMAX_MAX,
671 		"The following addition may not uphold the runtime assertion");
672 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
673 		assert(ctx->remaining < 0);
674 		return pldm_msgbuf_status(ctx, EOVERFLOW);
675 	}
676 	ctx->remaining -= sizeof(ldst);
677 	assert(ctx->remaining >= 0);
678 	if (ctx->remaining < 0) {
679 		return pldm_msgbuf_status(ctx, EOVERFLOW);
680 	}
681 
682 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
683 	ldst = le32toh(ldst);
684 	memcpy(dst, &ldst, sizeof(ldst));
685 	ctx->cursor += sizeof(ldst);
686 
687 	return 0;
688 }
689 
690 /**
691  * Extract the field at the msgbuf cursor into the lvalue named by dst.
692  *
693  * @param ctx The msgbuf context object
694  * @param dst The lvalue into which the field at the msgbuf cursor should be
695  *            extracted
696  *
697  * @return PLDM_SUCCESS on success, otherwise another value on error
698  */
699 #define pldm_msgbuf_extract(ctx, dst)                                          \
700 	_Generic((dst),                                                        \
701 		uint8_t: pldm__msgbuf_extract_uint8,                           \
702 		int8_t: pldm__msgbuf_extract_int8,                             \
703 		uint16_t: pldm__msgbuf_extract_uint16,                         \
704 		int16_t: pldm__msgbuf_extract_int16,                           \
705 		uint32_t: pldm__msgbuf_extract_uint32,                         \
706 		int32_t: pldm__msgbuf_extract_int32,                           \
707 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
708 
709 /**
710  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
711  *
712  * @param ctx The msgbuf context object
713  * @param dst The pointer to the object into which the field at the msgbuf
714  *            cursor should be extracted
715  *
716  * @return PLDM_SUCCESS on success, otherwise another value on error
717  */
718 #define pldm_msgbuf_extract_p(ctx, dst)                                        \
719 	_Generic((dst),                                                        \
720 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
721 		int8_t *: pldm__msgbuf_extract_int8,                           \
722 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
723 		int16_t *: pldm__msgbuf_extract_int16,                         \
724 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
725 		int32_t *: pldm__msgbuf_extract_int32,                         \
726 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
727 
728 __attribute__((always_inline)) static inline int
729 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
730 pldm__msgbuf_extract_array_void(struct pldm_msgbuf *ctx, void *dst,
731 				size_t count)
732 {
733 	assert(ctx);
734 
735 	if (!ctx->cursor || !dst) {
736 		return pldm_msgbuf_status(ctx, EINVAL);
737 	}
738 
739 	if (!count) {
740 		return 0;
741 	}
742 
743 #if INTMAX_MAX < SIZE_MAX
744 	if (count > INTMAX_MAX) {
745 		return pldm_msgbuf_status(ctx, EOVERFLOW);
746 	}
747 #endif
748 
749 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
750 		return pldm_msgbuf_status(ctx, EOVERFLOW);
751 	}
752 	ctx->remaining -= (intmax_t)count;
753 	assert(ctx->remaining >= 0);
754 	if (ctx->remaining < 0) {
755 		return pldm_msgbuf_status(ctx, EOVERFLOW);
756 	}
757 
758 	memcpy(dst, ctx->cursor, count);
759 	ctx->cursor += count;
760 
761 	return 0;
762 }
763 
764 __attribute__((always_inline)) static inline int
765 pldm_msgbuf_extract_array_char(struct pldm_msgbuf *ctx, char *dst, size_t count)
766 {
767 	return pldm__msgbuf_extract_array_void(ctx, dst, count);
768 }
769 
770 __attribute__((always_inline)) static inline int
771 pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, uint8_t *dst,
772 				size_t count)
773 {
774 	return pldm__msgbuf_extract_array_void(ctx, dst, count);
775 }
776 
777 #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
778 	_Generic((*(dst)),                                                     \
779 		uint8_t: pldm_msgbuf_extract_array_uint8,                      \
780 		char: pldm_msgbuf_extract_array_char)(ctx, dst, count)
781 
782 __attribute__((always_inline)) static inline int
783 pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, const uint32_t src)
784 {
785 	uint32_t val = htole32(src);
786 
787 	assert(ctx);
788 
789 	if (!ctx->cursor) {
790 		return pldm_msgbuf_status(ctx, EINVAL);
791 	}
792 
793 	static_assert(
794 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
795 		sizeof(src) < INTMAX_MAX,
796 		"The following addition may not uphold the runtime assertion");
797 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
798 		assert(ctx->remaining < 0);
799 		return pldm_msgbuf_status(ctx, EOVERFLOW);
800 	}
801 	ctx->remaining -= sizeof(src);
802 	assert(ctx->remaining >= 0);
803 	if (ctx->remaining < 0) {
804 		return pldm_msgbuf_status(ctx, EOVERFLOW);
805 	}
806 
807 	memcpy(ctx->cursor, &val, sizeof(val));
808 	ctx->cursor += sizeof(src);
809 
810 	return 0;
811 }
812 
813 __attribute__((always_inline)) static inline int
814 pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, const uint16_t src)
815 {
816 	uint16_t val = htole16(src);
817 
818 	assert(ctx);
819 
820 	if (!ctx->cursor) {
821 		return pldm_msgbuf_status(ctx, EINVAL);
822 	}
823 
824 	static_assert(
825 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
826 		sizeof(src) < INTMAX_MAX,
827 		"The following addition may not uphold the runtime assertion");
828 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
829 		assert(ctx->remaining < 0);
830 		return pldm_msgbuf_status(ctx, EOVERFLOW);
831 	}
832 	ctx->remaining -= sizeof(src);
833 	assert(ctx->remaining >= 0);
834 	if (ctx->remaining < 0) {
835 		return pldm_msgbuf_status(ctx, EOVERFLOW);
836 	}
837 
838 	memcpy(ctx->cursor, &val, sizeof(val));
839 	ctx->cursor += sizeof(src);
840 
841 	return 0;
842 }
843 
844 __attribute__((always_inline)) static inline int
845 pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, const uint8_t src)
846 {
847 	assert(ctx);
848 
849 	if (!ctx->cursor) {
850 		return pldm_msgbuf_status(ctx, EINVAL);
851 	}
852 
853 	static_assert(
854 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
855 		sizeof(src) < INTMAX_MAX,
856 		"The following addition may not uphold the runtime assertion");
857 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
858 		assert(ctx->remaining < 0);
859 		return pldm_msgbuf_status(ctx, EOVERFLOW);
860 	}
861 	ctx->remaining -= sizeof(src);
862 	assert(ctx->remaining >= 0);
863 	if (ctx->remaining < 0) {
864 		return pldm_msgbuf_status(ctx, EOVERFLOW);
865 	}
866 
867 	memcpy(ctx->cursor, &src, sizeof(src));
868 	ctx->cursor += sizeof(src);
869 
870 	return 0;
871 }
872 
873 __attribute__((always_inline)) static inline int
874 pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, const int32_t src)
875 {
876 	int32_t val = htole32(src);
877 
878 	assert(ctx);
879 
880 	if (!ctx->cursor) {
881 		return pldm_msgbuf_status(ctx, EINVAL);
882 	}
883 
884 	static_assert(
885 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
886 		sizeof(src) < INTMAX_MAX,
887 		"The following addition may not uphold the runtime assertion");
888 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
889 		assert(ctx->remaining < 0);
890 		return pldm_msgbuf_status(ctx, EOVERFLOW);
891 	}
892 	ctx->remaining -= sizeof(src);
893 	assert(ctx->remaining >= 0);
894 	if (ctx->remaining < 0) {
895 		return pldm_msgbuf_status(ctx, EOVERFLOW);
896 	}
897 
898 	memcpy(ctx->cursor, &val, sizeof(val));
899 	ctx->cursor += sizeof(src);
900 
901 	return 0;
902 }
903 
904 __attribute__((always_inline)) static inline int
905 pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, const int16_t src)
906 {
907 	int16_t val = htole16(src);
908 
909 	assert(ctx);
910 
911 	if (!ctx->cursor) {
912 		return pldm_msgbuf_status(ctx, EINVAL);
913 	}
914 
915 	static_assert(
916 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
917 		sizeof(src) < INTMAX_MAX,
918 		"The following addition may not uphold the runtime assertion");
919 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
920 		assert(ctx->remaining < 0);
921 		return pldm_msgbuf_status(ctx, EOVERFLOW);
922 	}
923 	ctx->remaining -= sizeof(src);
924 	assert(ctx->remaining >= 0);
925 	if (ctx->remaining < 0) {
926 		return pldm_msgbuf_status(ctx, EOVERFLOW);
927 	}
928 
929 	memcpy(ctx->cursor, &val, sizeof(val));
930 	ctx->cursor += sizeof(src);
931 
932 	return 0;
933 }
934 
935 __attribute__((always_inline)) static inline int
936 pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, const int8_t src)
937 {
938 	assert(ctx);
939 
940 	if (!ctx->cursor) {
941 		return pldm_msgbuf_status(ctx, EINVAL);
942 	}
943 
944 	static_assert(
945 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
946 		sizeof(src) < INTMAX_MAX,
947 		"The following addition may not uphold the runtime assertion");
948 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
949 		assert(ctx->remaining < 0);
950 		return pldm_msgbuf_status(ctx, EOVERFLOW);
951 	}
952 	ctx->remaining -= sizeof(src);
953 	assert(ctx->remaining >= 0);
954 	if (ctx->remaining < 0) {
955 		return pldm_msgbuf_status(ctx, EOVERFLOW);
956 	}
957 
958 	memcpy(ctx->cursor, &src, sizeof(src));
959 	ctx->cursor += sizeof(src);
960 
961 	return 0;
962 }
963 
964 #define pldm_msgbuf_insert(dst, src)                                           \
965 	_Generic((src),                                                        \
966 		uint8_t: pldm_msgbuf_insert_uint8,                             \
967 		int8_t: pldm_msgbuf_insert_int8,                               \
968 		uint16_t: pldm_msgbuf_insert_uint16,                           \
969 		int16_t: pldm_msgbuf_insert_int16,                             \
970 		uint32_t: pldm_msgbuf_insert_uint32,                           \
971 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
972 
973 __attribute__((always_inline)) static inline int
974 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
975 pldm__msgbuf_insert_array_void(struct pldm_msgbuf *ctx, const void *src,
976 			       size_t count)
977 {
978 	assert(ctx);
979 
980 	if (!ctx->cursor || !src) {
981 		return pldm_msgbuf_status(ctx, EINVAL);
982 	}
983 
984 	if (!count) {
985 		return 0;
986 	}
987 
988 #if INTMAX_MAX < SIZE_MAX
989 	if (count > INTMAX_MAX) {
990 		return pldm_msgbuf_status(ctx, EOVERFLOW);
991 	}
992 #endif
993 
994 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
995 		return pldm_msgbuf_status(ctx, EOVERFLOW);
996 	}
997 	ctx->remaining -= (intmax_t)count;
998 	assert(ctx->remaining >= 0);
999 	if (ctx->remaining < 0) {
1000 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1001 	}
1002 
1003 	memcpy(ctx->cursor, src, count);
1004 	ctx->cursor += count;
1005 
1006 	return 0;
1007 }
1008 
1009 __attribute__((always_inline)) static inline int
1010 pldm_msgbuf_insert_array_char(struct pldm_msgbuf *ctx, const char *src,
1011 			      size_t count)
1012 {
1013 	return pldm__msgbuf_insert_array_void(ctx, src, count);
1014 }
1015 
1016 __attribute__((always_inline)) static inline int
1017 pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, const uint8_t *src,
1018 			       size_t count)
1019 {
1020 	return pldm__msgbuf_insert_array_void(ctx, src, count);
1021 }
1022 
1023 #define pldm_msgbuf_insert_array(dst, src, count)                              \
1024 	_Generic((*(src)),                                                     \
1025 		uint8_t: pldm_msgbuf_insert_array_uint8,                       \
1026 		char: pldm_msgbuf_insert_array_char)(dst, src, count)
1027 
1028 __attribute__((always_inline)) static inline int
1029 pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, size_t required,
1030 			  void **cursor)
1031 {
1032 	assert(ctx);
1033 
1034 	if (!ctx->cursor || !cursor || *cursor) {
1035 		return pldm_msgbuf_status(ctx, EINVAL);
1036 	}
1037 
1038 #if INTMAX_MAX < SIZE_MAX
1039 	if (required > INTMAX_MAX) {
1040 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1041 	}
1042 #endif
1043 
1044 	if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
1045 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1046 	}
1047 	ctx->remaining -= (intmax_t)required;
1048 	assert(ctx->remaining >= 0);
1049 	if (ctx->remaining < 0) {
1050 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1051 	}
1052 
1053 	*cursor = ctx->cursor;
1054 	ctx->cursor += required;
1055 
1056 	return 0;
1057 }
1058 
1059 __attribute__((always_inline)) static inline int
1060 pldm_msgbuf_span_string_ascii(struct pldm_msgbuf *ctx, void **cursor,
1061 			      size_t *length)
1062 {
1063 	intmax_t measured;
1064 
1065 	assert(ctx);
1066 
1067 	if (!ctx->cursor || (cursor && *cursor)) {
1068 		return pldm_msgbuf_status(ctx, EINVAL);
1069 	}
1070 
1071 	if (ctx->remaining < 0) {
1072 		/* Tracking the amount of overflow gets disturbed here */
1073 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1074 	}
1075 
1076 	measured = (intmax_t)strnlen((const char *)ctx->cursor, ctx->remaining);
1077 	if (measured == ctx->remaining) {
1078 		/*
1079 		 * We have hit the end of the buffer prior to the NUL terminator.
1080 		 * Optimistically, the NUL terminator was one-beyond-the-end. Setting
1081 		 * ctx->remaining negative ensures the `pldm_msgbuf_destroy*()` APIs also
1082 		 * return an error.
1083 		 */
1084 		ctx->remaining = -1;
1085 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1086 	}
1087 
1088 	/* Include the NUL terminator in the span length, as spans are opaque */
1089 	measured++;
1090 
1091 	if (ctx->remaining < INTMAX_MIN + measured) {
1092 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1093 	}
1094 
1095 	ctx->remaining -= measured;
1096 	assert(ctx->remaining >= 0);
1097 	if (ctx->remaining < 0) {
1098 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1099 	}
1100 
1101 	if (cursor) {
1102 		*cursor = ctx->cursor;
1103 	}
1104 
1105 	ctx->cursor += measured;
1106 
1107 	if (length) {
1108 		*length = measured;
1109 	}
1110 
1111 	return 0;
1112 }
1113 
1114 __attribute__((always_inline)) static inline int
1115 pldm_msgbuf_span_string_utf16(struct pldm_msgbuf *ctx, void **cursor,
1116 			      size_t *length)
1117 {
1118 	static const char16_t term = 0;
1119 	ptrdiff_t measured;
1120 	void *end;
1121 
1122 	assert(ctx);
1123 
1124 	if (!ctx->cursor || (cursor && *cursor)) {
1125 		return pldm_msgbuf_status(ctx, EINVAL);
1126 	}
1127 
1128 	if (ctx->remaining < 0) {
1129 		/* Tracking the amount of overflow gets disturbed here */
1130 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1131 	}
1132 
1133 	/*
1134 	 * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do
1135 	 * not form a UTF16 NUL _code-point_ due to alignment with respect to the
1136 	 * start of the string
1137 	 */
1138 	end = ctx->cursor;
1139 	do {
1140 		if (end != ctx->cursor) {
1141 			/*
1142 			 * If we've looped we've found a relatively-unaligned NUL code-point.
1143 			 * Scan again from a relatively-aligned start point.
1144 			 */
1145 			end = (char *)end + 1;
1146 		}
1147 		measured = (char *)end - (char *)ctx->cursor;
1148 		end = memmem(end, ctx->remaining - measured, &term,
1149 			     sizeof(term));
1150 	} while (end && ((uintptr_t)end & 1) != ((uintptr_t)ctx->cursor & 1));
1151 
1152 	if (!end) {
1153 		/*
1154 		 * Optimistically, the last required pattern byte was one beyond the end of
1155 		 * the buffer. Setting ctx->remaining negative ensures the
1156 		 * `pldm_msgbuf_destroy*()` APIs also return an error.
1157 		 */
1158 		ctx->remaining = -1;
1159 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1160 	}
1161 
1162 	end = (char *)end + sizeof(char16_t);
1163 	measured = (char *)end - (char *)ctx->cursor;
1164 
1165 #if INTMAX_MAX < PTRDIFF_MAX
1166 	if (measured >= INTMAX_MAX) {
1167 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1168 	}
1169 #endif
1170 
1171 	if (ctx->remaining < INTMAX_MIN + (intmax_t)measured) {
1172 		assert(ctx->remaining < 0);
1173 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1174 	}
1175 
1176 	ctx->remaining -= (intmax_t)measured;
1177 	assert(ctx->remaining >= 0);
1178 	if (ctx->remaining < 0) {
1179 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1180 	}
1181 
1182 	if (cursor) {
1183 		*cursor = ctx->cursor;
1184 	}
1185 
1186 	ctx->cursor += measured;
1187 
1188 	if (length) {
1189 		*length = (size_t)measured;
1190 	}
1191 
1192 	return 0;
1193 }
1194 
1195 __attribute__((always_inline)) static inline int
1196 pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
1197 {
1198 	assert(ctx);
1199 
1200 	if (!ctx->cursor || !cursor || *cursor || !len) {
1201 		return pldm_msgbuf_status(ctx, EINVAL);
1202 	}
1203 
1204 	assert(ctx->remaining >= 0);
1205 	if (ctx->remaining < 0) {
1206 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1207 	}
1208 
1209 	*cursor = ctx->cursor;
1210 	ctx->cursor += ctx->remaining;
1211 	*len = ctx->remaining;
1212 	ctx->remaining = 0;
1213 
1214 	return 0;
1215 }
1216 
1217 /**
1218  * @brief pldm_msgbuf copy data between two msg buffers
1219  *
1220  * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1221  * @param[in,out] dst - destination of copy from source
1222  * @param[in] size - size of data to be copied
1223  * @param[in] description - description of data copied
1224  *
1225  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1226  * PLDM_ERROR_INVALID_LENGTH otherwise.
1227  * PLDM_ERROR_INVALID_DATA if input is invalid
1228  */
1229 #define pldm_msgbuf_copy(dst, src, type, name)                                 \
1230 	pldm__msgbuf_copy(dst, src, sizeof(type), #name)
1231 __attribute__((always_inline)) static inline int
1232 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
1233 pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1234 		  const char *description)
1235 {
1236 	assert(src);
1237 	assert(dst);
1238 	assert(src->mode == dst->mode);
1239 
1240 	if (!src->cursor || !dst->cursor || !description) {
1241 		return pldm_msgbuf_status(dst, EINVAL);
1242 	}
1243 
1244 #if INTMAX_MAX < SIZE_MAX
1245 	if (size > INTMAX_MAX) {
1246 		return pldm_msgbuf_status(dst, EOVERFLOW);
1247 	}
1248 #endif
1249 
1250 	if (src->remaining < INTMAX_MIN + (intmax_t)size) {
1251 		return pldm_msgbuf_status(dst, EOVERFLOW);
1252 	}
1253 
1254 	if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
1255 		return pldm_msgbuf_status(dst, EOVERFLOW);
1256 	}
1257 
1258 	src->remaining -= (intmax_t)size;
1259 	assert(src->remaining >= 0);
1260 	if (src->remaining < 0) {
1261 		return pldm_msgbuf_status(dst, EOVERFLOW);
1262 	}
1263 
1264 	dst->remaining -= (intmax_t)size;
1265 	assert(dst->remaining >= 0);
1266 	if (dst->remaining < 0) {
1267 		return pldm_msgbuf_status(dst, EOVERFLOW);
1268 	}
1269 
1270 	memcpy(dst->cursor, src->cursor, size);
1271 	src->cursor += size;
1272 	dst->cursor += size;
1273 
1274 	return 0;
1275 }
1276 
1277 __attribute__((always_inline)) static inline int
1278 pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1279 {
1280 	void *ascii = NULL;
1281 	size_t len = 0;
1282 	int rc;
1283 
1284 	rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len);
1285 	if (rc < 0) {
1286 		return rc;
1287 	}
1288 
1289 	return pldm__msgbuf_insert_array_void(dst, ascii, len);
1290 }
1291 
1292 __attribute__((always_inline)) static inline int
1293 pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1294 {
1295 	void *utf16 = NULL;
1296 	size_t len = 0;
1297 	int rc;
1298 
1299 	rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len);
1300 	if (rc < 0) {
1301 		return rc;
1302 	}
1303 
1304 	return pldm__msgbuf_insert_array_void(dst, utf16, len);
1305 }
1306 
1307 #ifdef __cplusplus
1308 }
1309 #endif
1310 
1311 #ifdef __cplusplus
1312 #include <type_traits>
1313 
1314 template <typename T>
1315 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
1316 						void *buf)
1317 {
1318 	static_assert(std::is_same<uint8_t *, T>::value);
1319 	return pldm__msgbuf_extract_uint8(ctx, buf);
1320 }
1321 
1322 template <typename T>
1323 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
1324 					       void *buf)
1325 {
1326 	static_assert(std::is_same<int8_t *, T>::value);
1327 	return pldm__msgbuf_extract_int8(ctx, buf);
1328 }
1329 
1330 template <typename T>
1331 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
1332 						 void *buf)
1333 {
1334 	static_assert(std::is_same<uint16_t *, T>::value);
1335 	return pldm__msgbuf_extract_uint16(ctx, buf);
1336 }
1337 
1338 template <typename T>
1339 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
1340 						void *buf)
1341 {
1342 	static_assert(std::is_same<int16_t *, T>::value);
1343 	return pldm__msgbuf_extract_int16(ctx, buf);
1344 }
1345 
1346 template <typename T>
1347 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
1348 						 void *buf)
1349 {
1350 	static_assert(std::is_same<uint32_t *, T>::value);
1351 	return pldm__msgbuf_extract_uint32(ctx, buf);
1352 }
1353 
1354 template <typename T>
1355 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
1356 						void *buf)
1357 {
1358 	static_assert(std::is_same<int32_t *, T>::value);
1359 	return pldm__msgbuf_extract_int32(ctx, buf);
1360 }
1361 
1362 template <typename T>
1363 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
1364 						 void *buf)
1365 {
1366 	static_assert(std::is_same<real32_t *, T>::value);
1367 	return pldm__msgbuf_extract_real32(ctx, buf);
1368 }
1369 #endif
1370 
1371 #endif /* BUF_H */
1372