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