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_ARGS(1)
1145 LIBPLDM_CC_ALWAYS_INLINE
pldm_msgbuf_span_until(struct pldm_msgbuf * ctx,size_t trailer,void ** cursor,size_t * length)1146 int pldm_msgbuf_span_until(struct pldm_msgbuf *ctx, size_t trailer,
1147 void **cursor, size_t *length)
1148 {
1149 #if INTMAX_MAX < SIZE_MAX
1150 if (trailer > INTMAX_MAX) {
1151 return pldm__msgbuf_invalidate(ctx);
1152 }
1153 #endif
1154
1155 if (ctx->remaining >= (intmax_t)trailer) {
1156 ptrdiff_t delta;
1157
1158 assert(ctx->cursor);
1159
1160 delta = ctx->remaining - (intmax_t)trailer;
1161 if (cursor) {
1162 *cursor = ctx->cursor;
1163 }
1164 ctx->cursor += delta;
1165 if (length) {
1166 *length = delta;
1167 }
1168 ctx->remaining = (intmax_t)trailer;
1169 return 0;
1170 }
1171
1172 if (ctx->remaining > INTMAX_MIN + (intmax_t)trailer) {
1173 ctx->remaining = INTMAX_MIN + (intmax_t)trailer;
1174 return -EOVERFLOW;
1175 }
1176
1177 return pldm__msgbuf_invalidate(ctx);
1178 }
1179
1180 LIBPLDM_CC_NONNULL
1181 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_peek_remaining(struct pldm_msgbuf * ctx,void ** cursor,size_t * len)1182 pldm_msgbuf_peek_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
1183 {
1184 if (ctx->remaining < 0) {
1185 return -EOVERFLOW;
1186 }
1187
1188 assert(ctx->cursor);
1189 *cursor = ctx->cursor;
1190 *len = ctx->remaining;
1191
1192 return 0;
1193 }
1194
1195 LIBPLDM_CC_NONNULL
pldm_msgbuf_skip(struct pldm_msgbuf * ctx,size_t count)1196 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_skip(struct pldm_msgbuf *ctx,
1197 size_t count)
1198 {
1199 #if INTMAX_MAX < SIZE_MAX
1200 if (count > INTMAX_MAX) {
1201 return pldm__msgbuf_invalidate(ctx);
1202 }
1203 #endif
1204
1205 if (ctx->remaining >= (intmax_t)count) {
1206 assert(ctx->cursor);
1207 ctx->cursor += count;
1208 ctx->remaining -= (intmax_t)count;
1209 return 0;
1210 }
1211
1212 if (ctx->remaining > INTMAX_MIN + (intmax_t)count) {
1213 ctx->remaining -= (intmax_t)count;
1214 return -EOVERFLOW;
1215 }
1216
1217 return pldm__msgbuf_invalidate(ctx);
1218 }
1219
1220 /**
1221 * @brief Complete the pldm_msgbuf instance and return the number of bytes
1222 * consumed.
1223 *
1224 * @param ctx - The msgbuf.
1225 * @param orig_len - The original size of the msgbuf, the `len` argument passed to
1226 * pldm_msgbuf_init_errno().
1227 * @param ret_used_len - The number of bytes that have been used from the msgbuf instance.
1228 *
1229 * This can be called after a number of pldm_msgbuf_insert...() calls to
1230 * determine the total size that was written.
1231 *
1232 * @return 0 on success, -EOVERFLOW if an implausible orig_len was provided or
1233 * an out-of-bounds access occurred.
1234 */
1235 LIBPLDM_CC_NONNULL
1236 LIBPLDM_CC_ALWAYS_INLINE
1237 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_complete_used(struct pldm_msgbuf * ctx,size_t orig_len,size_t * ret_used_len)1238 int pldm_msgbuf_complete_used(struct pldm_msgbuf *ctx, size_t orig_len,
1239 size_t *ret_used_len)
1240 {
1241 int rc;
1242
1243 ctx->cursor = NULL;
1244 rc = pldm_msgbuf_validate(ctx);
1245 if (rc) {
1246 pldm__msgbuf_invalidate(ctx);
1247 return rc;
1248 }
1249
1250 if ((size_t)ctx->remaining > orig_len) {
1251 /* Caller passed incorrect orig_len */
1252 return pldm__msgbuf_invalidate(ctx);
1253 }
1254
1255 *ret_used_len = orig_len - ctx->remaining;
1256 pldm__msgbuf_invalidate(ctx);
1257 return 0;
1258 }
1259
1260 /**
1261 * @brief pldm_msgbuf copy data between two msg buffers
1262 *
1263 * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1264 * @param[in,out] dst - destination of copy from source
1265 * @param[in] size - size of data to be copied
1266 * @param[in] description - description of data copied
1267 *
1268 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1269 * PLDM_ERROR_INVALID_LENGTH otherwise.
1270 * PLDM_ERROR_INVALID_DATA if input is invalid
1271 */
1272 #define pldm_msgbuf_copy(dst, src, type, name) \
1273 pldm__msgbuf_copy(dst, src, sizeof(type), #name)
1274 LIBPLDM_CC_NONNULL
1275 LIBPLDM_CC_ALWAYS_INLINE int
1276 // 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)1277 pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1278 const char *description LIBPLDM_CC_UNUSED)
1279 {
1280 #if INTMAX_MAX < SIZE_MAX
1281 if (size > INTMAX_MAX) {
1282 pldm__msgbuf_invalidate(src);
1283 pldm__msgbuf_invalidate(dst);
1284 return -EOVERFLOW;
1285 }
1286 #endif
1287
1288 if (src->remaining >= (intmax_t)size &&
1289 dst->remaining >= (intmax_t)size) {
1290 assert(src->cursor && dst->cursor);
1291 memcpy(dst->cursor, src->cursor, size);
1292 src->cursor += size;
1293 src->remaining -= (intmax_t)size;
1294 dst->cursor += size;
1295 dst->remaining -= (intmax_t)size;
1296 return 0;
1297 }
1298
1299 if (src->remaining > INTMAX_MIN + (intmax_t)size) {
1300 src->remaining -= (intmax_t)size;
1301 } else {
1302 pldm__msgbuf_invalidate(src);
1303 }
1304
1305 if (dst->remaining > INTMAX_MIN + (intmax_t)size) {
1306 dst->remaining -= (intmax_t)size;
1307 } else {
1308 pldm__msgbuf_invalidate(dst);
1309 }
1310
1311 return -EOVERFLOW;
1312 }
1313
1314 LIBPLDM_CC_NONNULL
1315 LIBPLDM_CC_WARN_UNUSED_RESULT
1316 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf * dst,struct pldm_msgbuf * src)1317 pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1318 {
1319 void *ascii = NULL;
1320 size_t len = 0;
1321 int rc;
1322
1323 rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len);
1324 if (rc < 0) {
1325 return rc;
1326 }
1327
1328 return pldm__msgbuf_insert_array_void(dst, len, ascii, len);
1329 }
1330
1331 LIBPLDM_CC_NONNULL
1332 LIBPLDM_CC_WARN_UNUSED_RESULT
1333 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf * dst,struct pldm_msgbuf * src)1334 pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1335 {
1336 void *utf16 = NULL;
1337 size_t len = 0;
1338 int rc;
1339
1340 rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len);
1341 if (rc < 0) {
1342 return rc;
1343 }
1344
1345 return pldm__msgbuf_insert_array_void(dst, len, utf16, len);
1346 }
1347
1348 /**
1349 * @brief pldm_msgbuf uint8_t extractor for a size_t
1350 *
1351 * @param[in,out] ctx - pldm_msgbuf context for extractor
1352 * @param[out] dst - destination of extracted value
1353 *
1354 * @return 0 if buffer accesses were in-bounds,
1355 * -EINVAL if dst pointer is invalid,
1356 * -EOVERFLOW is the buffer was out of bound.
1357 */
1358 #define pldm_msgbuf_extract_uint8_to_size(ctx, dst) \
1359 pldm__msgbuf_extract_uint8_to_size(ctx, &(dst))
1360 LIBPLDM_CC_NONNULL
1361 LIBPLDM_CC_ALWAYS_INLINE int
1362 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint8_to_size(struct pldm_msgbuf * ctx,size_t * dst)1363 pldm__msgbuf_extract_uint8_to_size(struct pldm_msgbuf *ctx, size_t *dst)
1364 {
1365 uint8_t value;
1366 int rc;
1367
1368 rc = pldm__msgbuf_extract_uint8(ctx, &value);
1369 if (rc) {
1370 return rc;
1371 }
1372
1373 static_assert(SIZE_MAX >= UINT8_MAX, "Invalid promotion");
1374
1375 *dst = value;
1376 return 0;
1377 }
1378
1379 /**
1380 * @brief pldm_msgbuf uint16_t extractor for a size_t
1381 *
1382 * @param[in,out] ctx - pldm_msgbuf context for extractor
1383 * @param[out] dst - destination of extracted value
1384 *
1385 * @return 0 if buffer accesses were in-bounds,
1386 * -EINVAL if dst pointer is invalid,
1387 * -EOVERFLOW is the buffer was out of bound.
1388 */
1389 #define pldm_msgbuf_extract_uint16_to_size(ctx, dst) \
1390 pldm__msgbuf_extract_uint16_to_size(ctx, &(dst))
1391 LIBPLDM_CC_NONNULL
1392 LIBPLDM_CC_ALWAYS_INLINE int
1393 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint16_to_size(struct pldm_msgbuf * ctx,size_t * dst)1394 pldm__msgbuf_extract_uint16_to_size(struct pldm_msgbuf *ctx, size_t *dst)
1395 {
1396 uint16_t value;
1397 int rc;
1398
1399 rc = pldm__msgbuf_extract_uint16(ctx, &value);
1400 if (rc) {
1401 return rc;
1402 }
1403
1404 static_assert(SIZE_MAX >= UINT16_MAX, "Invalid promotion");
1405
1406 *dst = value;
1407 return 0;
1408 }
1409
1410 /**
1411 * @brief pldm_msgbuf uint32_t extractor for a size_t
1412 *
1413 * @param[in,out] ctx - pldm_msgbuf context for extractor
1414 * @param[out] dst - destination of extracted value
1415 *
1416 * @return 0 if buffer accesses were in-bounds,
1417 * -EINVAL if dst pointer is invalid,
1418 * -EOVERFLOW is the buffer was out of bound.
1419 */
1420 #define pldm_msgbuf_extract_uint32_to_size(ctx, dst) \
1421 pldm__msgbuf_extract_uint32_to_size(ctx, &(dst))
1422 LIBPLDM_CC_NONNULL
1423 LIBPLDM_CC_ALWAYS_INLINE int
1424 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint32_to_size(struct pldm_msgbuf * ctx,size_t * dst)1425 pldm__msgbuf_extract_uint32_to_size(struct pldm_msgbuf *ctx, size_t *dst)
1426 {
1427 uint32_t value;
1428 int rc;
1429
1430 rc = pldm__msgbuf_extract_uint32(ctx, &value);
1431 if (rc) {
1432 return rc;
1433 }
1434
1435 static_assert(SIZE_MAX >= UINT32_MAX, "Invalid promotion");
1436
1437 *dst = value;
1438 return 0;
1439 }
1440
1441 #ifdef __cplusplus
1442 }
1443 #endif
1444
1445 #ifdef __cplusplus
1446 #include <type_traits>
1447
1448 template <typename T>
pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf * ctx,void * buf)1449 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
1450 void *buf)
1451 {
1452 static_assert(std::is_same<uint8_t, T>::value);
1453 return pldm__msgbuf_extract_uint8(ctx, buf);
1454 }
1455
1456 template <typename T>
pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf * ctx,void * buf)1457 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
1458 void *buf)
1459 {
1460 static_assert(std::is_same<int8_t, T>::value);
1461 return pldm__msgbuf_extract_int8(ctx, buf);
1462 }
1463
1464 template <typename T>
pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf * ctx,void * buf)1465 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
1466 void *buf)
1467 {
1468 static_assert(std::is_same<uint16_t, T>::value);
1469 return pldm__msgbuf_extract_uint16(ctx, buf);
1470 }
1471
1472 template <typename T>
pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf * ctx,void * buf)1473 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
1474 void *buf)
1475 {
1476 static_assert(std::is_same<int16_t, T>::value);
1477 return pldm__msgbuf_extract_int16(ctx, buf);
1478 }
1479
1480 template <typename T>
pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf * ctx,void * buf)1481 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
1482 void *buf)
1483 {
1484 static_assert(std::is_same<uint32_t, T>::value);
1485 return pldm__msgbuf_extract_uint32(ctx, buf);
1486 }
1487
1488 template <typename T>
pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf * ctx,void * buf)1489 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
1490 void *buf)
1491 {
1492 static_assert(std::is_same<int32_t, T>::value);
1493 return pldm__msgbuf_extract_int32(ctx, buf);
1494 }
1495
1496 template <typename T>
pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf * ctx,void * buf)1497 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
1498 void *buf)
1499 {
1500 static_assert(std::is_same<real32_t, T>::value);
1501 return pldm__msgbuf_extract_real32(ctx, buf);
1502 }
1503 #endif
1504
1505 #endif /* BUF_H */
1506