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