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