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