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