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