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