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