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