1 /* 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
40 extern "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 */
60 static 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
69 struct pldm_msgbuf_rw {
70 uint8_t *cursor;
71 intmax_t remaining;
72 };
73
74 struct pldm_msgbuf_ro {
75 const uint8_t *cursor;
76 intmax_t remaining;
77 };
78
79 LIBPLDM_CC_ALWAYS_INLINE
80 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_cleanup(const void * cursor LIBPLDM_CC_UNUSED,intmax_t remaining LIBPLDM_CC_UNUSED)81 void 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
87 LIBPLDM_CC_NONNULL
88 LIBPLDM_CC_ALWAYS_INLINE
89 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_rw_cleanup(struct pldm_msgbuf_rw * ctx LIBPLDM_CC_UNUSED)90 void 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
95 LIBPLDM_CC_NONNULL
96 LIBPLDM_CC_ALWAYS_INLINE
97 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_ro_cleanup(struct pldm_msgbuf_ro * ctx LIBPLDM_CC_UNUSED)98 void 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
103 LIBPLDM_CC_NONNULL
104 LIBPLDM_CC_ALWAYS_INLINE
105 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_set_invalid(intmax_t * remaining)106 int pldm__msgbuf_set_invalid(intmax_t *remaining)
107 {
108 *remaining = INTMAX_MIN;
109 return -EOVERFLOW;
110 }
111
112 LIBPLDM_CC_NONNULL
113 LIBPLDM_CC_ALWAYS_INLINE
114 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_rw_invalidate(struct pldm_msgbuf_rw * ctx)115 int pldm__msgbuf_rw_invalidate(struct pldm_msgbuf_rw *ctx)
116 {
117 return pldm__msgbuf_set_invalid(&ctx->remaining);
118 }
119
120 LIBPLDM_CC_NONNULL
121 LIBPLDM_CC_ALWAYS_INLINE
122 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_ro_invalidate(struct pldm_msgbuf_ro * ctx)123 int pldm__msgbuf_ro_invalidate(struct pldm_msgbuf_ro *ctx)
124 {
125 return pldm__msgbuf_set_invalid(&ctx->remaining);
126 }
127
128 LIBPLDM_CC_NONNULL
129 LIBPLDM_CC_ALWAYS_INLINE
130 LIBPLDM_CC_WARN_UNUSED_RESULT
131 int
132 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_init_errno(const uint8_t ** cursor,intmax_t * remaining,size_t minsize,const void * buf,size_t len)133 pldm__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 */
169 LIBPLDM_CC_NONNULL
170 LIBPLDM_CC_ALWAYS_INLINE
171 LIBPLDM_CC_WARN_UNUSED_RESULT
172 int
173 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm_msgbuf_rw_init_errno(struct pldm_msgbuf_rw * ctx,size_t minsize,const void * buf,size_t len)174 pldm_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 */
192 LIBPLDM_CC_NONNULL
193 LIBPLDM_CC_ALWAYS_INLINE
194 LIBPLDM_CC_WARN_UNUSED_RESULT
195 int
196 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm_msgbuf_ro_init_errno(struct pldm_msgbuf_ro * ctx,size_t minsize,const void * buf,size_t len)197 pldm_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 */
215 LIBPLDM_CC_NONNULL
216 LIBPLDM_CC_ALWAYS_INLINE
217 LIBPLDM_CC_WARN_UNUSED_RESULT
218 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_validate(intmax_t remaining)219 int pldm__msgbuf_validate(intmax_t remaining)
220 {
221 if (remaining < 0) {
222 return -EOVERFLOW;
223 }
224
225 return 0;
226 }
227
228 LIBPLDM_CC_NONNULL
229 LIBPLDM_CC_ALWAYS_INLINE
230 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_ro_validate(struct pldm_msgbuf_ro * ctx)231 int pldm_msgbuf_ro_validate(struct pldm_msgbuf_ro *ctx)
232 {
233 return pldm__msgbuf_validate(ctx->remaining);
234 }
235
236 LIBPLDM_CC_NONNULL
237 LIBPLDM_CC_ALWAYS_INLINE
238 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_rw_validate(struct pldm_msgbuf_rw * ctx)239 int 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 */
254 LIBPLDM_CC_NONNULL
255 LIBPLDM_CC_ALWAYS_INLINE
256 LIBPLDM_CC_WARN_UNUSED_RESULT
257 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_consumed(intmax_t remaining)258 int 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
271 LIBPLDM_CC_NONNULL
272 LIBPLDM_CC_ALWAYS_INLINE
273 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_ro_consumed(struct pldm_msgbuf_ro * ctx)274 int pldm_msgbuf_ro_consumed(struct pldm_msgbuf_ro *ctx)
275 {
276 return pldm__msgbuf_consumed(ctx->remaining);
277 }
278
279 LIBPLDM_CC_NONNULL
280 LIBPLDM_CC_ALWAYS_INLINE
281 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_rw_consumed(struct pldm_msgbuf_rw * ctx)282 int 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 */
300 LIBPLDM_CC_NONNULL
301 LIBPLDM_CC_ALWAYS_INLINE
302 LIBPLDM_CC_WARN_UNUSED_RESULT
303 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_discard(const uint8_t ** cursor,intmax_t * remaining,int error)304 int 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
311 LIBPLDM_CC_NONNULL
312 LIBPLDM_CC_ALWAYS_INLINE
313 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_rw_discard(struct pldm_msgbuf_rw * ctx,int error)314 int 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
320 LIBPLDM_CC_NONNULL
321 LIBPLDM_CC_ALWAYS_INLINE
322 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_ro_discard(struct pldm_msgbuf_ro * ctx,int error)323 int 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 */
335 LIBPLDM_CC_NONNULL
336 LIBPLDM_CC_ALWAYS_INLINE
337 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_rw_complete(struct pldm_msgbuf_rw * ctx)338 int 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 */
350 LIBPLDM_CC_NONNULL
351 LIBPLDM_CC_ALWAYS_INLINE
352 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_ro_complete(struct pldm_msgbuf_ro * ctx)353 int 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 */
369 LIBPLDM_CC_NONNULL
370 LIBPLDM_CC_ALWAYS_INLINE
371 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_rw_complete_consumed(struct pldm_msgbuf_rw * ctx)372 int 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 */
388 LIBPLDM_CC_NONNULL
389 LIBPLDM_CC_ALWAYS_INLINE
390 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_ro_complete_consumed(struct pldm_msgbuf_ro * ctx)391 int 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 */
486 LIBPLDM_CC_NONNULL
487 LIBPLDM_CC_ALWAYS_INLINE int
488 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint8(struct pldm_msgbuf_ro * ctx,void * dst)489 pldm__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
507 LIBPLDM_CC_NONNULL
508 LIBPLDM_CC_ALWAYS_INLINE int
509 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int8(struct pldm_msgbuf_ro * ctx,void * dst)510 pldm__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
528 LIBPLDM_CC_NONNULL
529 LIBPLDM_CC_ALWAYS_INLINE int
530 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint16(struct pldm_msgbuf_ro * ctx,void * dst)531 pldm__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
567 LIBPLDM_CC_NONNULL
568 LIBPLDM_CC_ALWAYS_INLINE int
569 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int16(struct pldm_msgbuf_ro * ctx,void * dst)570 pldm__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
597 LIBPLDM_CC_NONNULL
598 LIBPLDM_CC_ALWAYS_INLINE int
599 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint32(struct pldm_msgbuf_ro * ctx,void * dst)600 pldm__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
627 LIBPLDM_CC_NONNULL
628 LIBPLDM_CC_ALWAYS_INLINE int
629 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int32(struct pldm_msgbuf_ro * ctx,void * dst)630 pldm__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
657 LIBPLDM_CC_NONNULL
658 LIBPLDM_CC_ALWAYS_INLINE int
659 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_real32(struct pldm_msgbuf_ro * ctx,void * dst)660 pldm__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
690 LIBPLDM_CC_NONNULL
691 LIBPLDM_CC_WARN_UNUSED_RESULT
692 LIBPLDM_CC_ALWAYS_INLINE int
693 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_array_void(struct pldm_msgbuf_ro * ctx,size_t count,void * dst,size_t dst_count)694 pldm__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 */
730 LIBPLDM_CC_NONNULL
731 LIBPLDM_CC_WARN_UNUSED_RESULT
732 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_extract_array_char(struct pldm_msgbuf_ro * ctx,size_t count,char * dst,size_t dst_count)733 pldm_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 */
742 LIBPLDM_CC_NONNULL
743 LIBPLDM_CC_WARN_UNUSED_RESULT
744 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf_ro * ctx,size_t count,uint8_t * dst,size_t dst_count)745 pldm_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
770 LIBPLDM_CC_NONNULL
771 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_uint64(struct pldm_msgbuf_rw * ctx,const uint64_t src)772 pldm_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
797 LIBPLDM_CC_NONNULL
798 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_real32(struct pldm_msgbuf_rw * ctx,const real32_t src)799 pldm_msgbuf_insert_real32(struct pldm_msgbuf_rw *ctx, const real32_t src)
800 {
801 uint32_t raw;
802 memcpy(&raw, &src, sizeof(raw));
803 uint32_t val = htole32(raw);
804
805 static_assert(
806 // NOLINTNEXTLINE(bugprone-sizeof-expression)
807 sizeof(src) < INTMAX_MAX,
808 "The following addition may not uphold the runtime assertion");
809
810 if (ctx->remaining >= (intmax_t)sizeof(src)) {
811 assert(ctx->cursor);
812 memcpy(ctx->cursor, &val, sizeof(val));
813 ctx->cursor += sizeof(src);
814 ctx->remaining -= sizeof(src);
815 return 0;
816 }
817
818 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) {
819 ctx->remaining -= sizeof(src);
820 return -EOVERFLOW;
821 }
822
823 return pldm__msgbuf_rw_invalidate(ctx);
824 }
825
826 LIBPLDM_CC_NONNULL
827 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_uint32(struct pldm_msgbuf_rw * ctx,const uint32_t src)828 pldm_msgbuf_insert_uint32(struct pldm_msgbuf_rw *ctx, const uint32_t src)
829 {
830 uint32_t val = htole32(src);
831
832 static_assert(
833 // NOLINTNEXTLINE(bugprone-sizeof-expression)
834 sizeof(src) < INTMAX_MAX,
835 "The following addition may not uphold the runtime assertion");
836
837 if (ctx->remaining >= (intmax_t)sizeof(src)) {
838 assert(ctx->cursor);
839 memcpy(ctx->cursor, &val, sizeof(val));
840 ctx->cursor += sizeof(src);
841 ctx->remaining -= sizeof(src);
842 return 0;
843 }
844
845 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) {
846 ctx->remaining -= sizeof(src);
847 return -EOVERFLOW;
848 }
849
850 return pldm__msgbuf_rw_invalidate(ctx);
851 }
852
853 LIBPLDM_CC_NONNULL
854 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_uint16(struct pldm_msgbuf_rw * ctx,const uint16_t src)855 pldm_msgbuf_insert_uint16(struct pldm_msgbuf_rw *ctx, const uint16_t src)
856 {
857 uint16_t val = htole16(src);
858
859 static_assert(
860 // NOLINTNEXTLINE(bugprone-sizeof-expression)
861 sizeof(src) < INTMAX_MAX,
862 "The following addition may not uphold the runtime assertion");
863
864 if (ctx->remaining >= (intmax_t)sizeof(src)) {
865 assert(ctx->cursor);
866 memcpy(ctx->cursor, &val, sizeof(val));
867 ctx->cursor += sizeof(src);
868 ctx->remaining -= sizeof(src);
869 return 0;
870 }
871
872 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) {
873 ctx->remaining -= sizeof(src);
874 return -EOVERFLOW;
875 }
876
877 return pldm__msgbuf_rw_invalidate(ctx);
878 }
879
880 LIBPLDM_CC_NONNULL
881 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_uint8(struct pldm_msgbuf_rw * ctx,const uint8_t src)882 pldm_msgbuf_insert_uint8(struct pldm_msgbuf_rw *ctx, const uint8_t src)
883 {
884 static_assert(
885 // NOLINTNEXTLINE(bugprone-sizeof-expression)
886 sizeof(src) < INTMAX_MAX,
887 "The following addition may not uphold the runtime assertion");
888
889 if (ctx->remaining >= (intmax_t)sizeof(src)) {
890 assert(ctx->cursor);
891 memcpy(ctx->cursor, &src, sizeof(src));
892 ctx->cursor += sizeof(src);
893 ctx->remaining -= sizeof(src);
894 return 0;
895 }
896
897 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) {
898 ctx->remaining -= sizeof(src);
899 return -EOVERFLOW;
900 }
901
902 return pldm__msgbuf_rw_invalidate(ctx);
903 }
904
905 LIBPLDM_CC_NONNULL
906 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_int32(struct pldm_msgbuf_rw * ctx,const int32_t src)907 pldm_msgbuf_insert_int32(struct pldm_msgbuf_rw *ctx, const int32_t src)
908 {
909 int32_t val = htole32(src);
910
911 static_assert(
912 // NOLINTNEXTLINE(bugprone-sizeof-expression)
913 sizeof(src) < INTMAX_MAX,
914 "The following addition may not uphold the runtime assertion");
915
916 if (ctx->remaining >= (intmax_t)sizeof(src)) {
917 assert(ctx->cursor);
918 memcpy(ctx->cursor, &val, sizeof(val));
919 ctx->cursor += sizeof(src);
920 ctx->remaining -= sizeof(src);
921 return 0;
922 }
923
924 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) {
925 ctx->remaining -= sizeof(src);
926 return -EOVERFLOW;
927 }
928
929 return pldm__msgbuf_rw_invalidate(ctx);
930 }
931
932 LIBPLDM_CC_NONNULL
933 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_int16(struct pldm_msgbuf_rw * ctx,const int16_t src)934 pldm_msgbuf_insert_int16(struct pldm_msgbuf_rw *ctx, const int16_t src)
935 {
936 int16_t val = htole16(src);
937
938 static_assert(
939 // NOLINTNEXTLINE(bugprone-sizeof-expression)
940 sizeof(src) < INTMAX_MAX,
941 "The following addition may not uphold the runtime assertion");
942
943 if (ctx->remaining >= (intmax_t)sizeof(src)) {
944 assert(ctx->cursor);
945 memcpy(ctx->cursor, &val, sizeof(val));
946 ctx->cursor += sizeof(src);
947 ctx->remaining -= sizeof(src);
948 return 0;
949 }
950
951 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) {
952 ctx->remaining -= sizeof(src);
953 return -EOVERFLOW;
954 }
955
956 return pldm__msgbuf_rw_invalidate(ctx);
957 }
958
959 LIBPLDM_CC_NONNULL
pldm_msgbuf_insert_int8(struct pldm_msgbuf_rw * ctx,const int8_t src)960 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int8(struct pldm_msgbuf_rw *ctx,
961 const int8_t src)
962 {
963 static_assert(
964 // NOLINTNEXTLINE(bugprone-sizeof-expression)
965 sizeof(src) < INTMAX_MAX,
966 "The following addition may not uphold the runtime assertion");
967
968 if (ctx->remaining >= (intmax_t)sizeof(src)) {
969 assert(ctx->cursor);
970 memcpy(ctx->cursor, &src, sizeof(src));
971 ctx->cursor += sizeof(src);
972 ctx->remaining -= sizeof(src);
973 return 0;
974 }
975
976 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) {
977 ctx->remaining -= sizeof(src);
978 return -EOVERFLOW;
979 }
980
981 return pldm__msgbuf_rw_invalidate(ctx);
982 }
983
984 LIBPLDM_CC_NONNULL
985 LIBPLDM_CC_WARN_UNUSED_RESULT
986 LIBPLDM_CC_ALWAYS_INLINE int
987 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_insert_array_void(struct pldm_msgbuf_rw * ctx,size_t count,const void * src,size_t src_count)988 pldm__msgbuf_insert_array_void(struct pldm_msgbuf_rw *ctx, size_t count,
989 const void *src, size_t src_count)
990 {
991 if (count > src_count) {
992 return -EINVAL;
993 }
994
995 if (!count) {
996 return 0;
997 }
998
999 #if INTMAX_MAX < SIZE_MAX
1000 if (count > INTMAX_MAX) {
1001 return pldm__msgbuf_rw_invalidate(ctx);
1002 }
1003 #endif
1004
1005 if (ctx->remaining >= (intmax_t)count) {
1006 assert(ctx->cursor);
1007 memcpy(ctx->cursor, src, count);
1008 ctx->cursor += count;
1009 ctx->remaining -= (intmax_t)count;
1010 return 0;
1011 }
1012
1013 if (ctx->remaining > INTMAX_MIN + (intmax_t)count) {
1014 ctx->remaining -= (intmax_t)count;
1015 return -EOVERFLOW;
1016 }
1017
1018 return pldm__msgbuf_rw_invalidate(ctx);
1019 }
1020
1021 LIBPLDM_CC_NONNULL
1022 LIBPLDM_CC_WARN_UNUSED_RESULT
1023 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_array_char(struct pldm_msgbuf_rw * ctx,size_t count,const char * src,size_t src_count)1024 pldm_msgbuf_insert_array_char(struct pldm_msgbuf_rw *ctx, size_t count,
1025 const char *src, size_t src_count)
1026 {
1027 return pldm__msgbuf_insert_array_void(ctx, count, src, src_count);
1028 }
1029
1030 LIBPLDM_CC_NONNULL
1031 LIBPLDM_CC_WARN_UNUSED_RESULT
1032 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf_rw * ctx,size_t count,const uint8_t * src,size_t src_count)1033 pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf_rw *ctx, size_t count,
1034 const uint8_t *src, size_t src_count)
1035 {
1036 return pldm__msgbuf_insert_array_void(ctx, count, src, src_count);
1037 }
1038
1039 LIBPLDM_CC_NONNULL_ARGS(1)
1040 LIBPLDM_CC_ALWAYS_INLINE int
1041 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_span_required(const uint8_t ** buf,intmax_t * remaining,size_t required,const void ** cursor)1042 pldm__msgbuf_span_required(const uint8_t **buf, intmax_t *remaining,
1043 size_t required, const void **cursor)
1044 {
1045 #if INTMAX_MAX < SIZE_MAX
1046 if (required > INTMAX_MAX) {
1047 return pldm__msgbuf_set_invalid(remaining);
1048 }
1049 #endif
1050
1051 if (*remaining >= (intmax_t)required) {
1052 assert(*buf);
1053 if (cursor) {
1054 *cursor = *buf;
1055 }
1056 *buf += required;
1057 *remaining -= (intmax_t)required;
1058 return 0;
1059 }
1060
1061 if (*remaining > INTMAX_MIN + (intmax_t)required) {
1062 *remaining -= (intmax_t)required;
1063 return -EOVERFLOW;
1064 }
1065
1066 return pldm__msgbuf_set_invalid(remaining);
1067 }
1068
1069 LIBPLDM_CC_NONNULL_ARGS(1)
1070 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_ro_span_required(struct pldm_msgbuf_ro * ctx,size_t required,const void ** cursor)1071 pldm_msgbuf_ro_span_required(struct pldm_msgbuf_ro *ctx, size_t required,
1072 const void **cursor)
1073 {
1074 return pldm__msgbuf_span_required(&ctx->cursor, &ctx->remaining,
1075 required, cursor);
1076 }
1077
1078 LIBPLDM_CC_NONNULL_ARGS(1)
1079 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_rw_span_required(struct pldm_msgbuf_rw * ctx,size_t required,void ** cursor)1080 pldm_msgbuf_rw_span_required(struct pldm_msgbuf_rw *ctx, size_t required,
1081 void **cursor)
1082 {
1083 return pldm__msgbuf_span_required((const uint8_t **)&ctx->cursor,
1084 &ctx->remaining, required,
1085 (const void **)cursor);
1086 }
1087
1088 LIBPLDM_CC_NONNULL_ARGS(1)
1089 LIBPLDM_CC_ALWAYS_INLINE int
1090 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_span_string_ascii(const uint8_t ** buf,intmax_t * remaining,const void ** cursor,size_t * length)1091 pldm__msgbuf_span_string_ascii(const uint8_t **buf, intmax_t *remaining,
1092 const void **cursor, size_t *length)
1093 {
1094 intmax_t measured;
1095
1096 if (*remaining < 0) {
1097 return pldm__msgbuf_set_invalid(remaining);
1098 }
1099 assert(*buf);
1100
1101 measured = (intmax_t)strnlen((const char *)*buf, *remaining);
1102 if (measured == *remaining) {
1103 return pldm__msgbuf_set_invalid(remaining);
1104 }
1105
1106 /* Include the NUL terminator in the span length, as spans are opaque */
1107 measured++;
1108
1109 if (*remaining >= measured) {
1110 assert(*buf);
1111 if (cursor) {
1112 *cursor = *buf;
1113 }
1114
1115 *buf += measured;
1116
1117 if (length) {
1118 *length = measured;
1119 }
1120
1121 *remaining -= measured;
1122 return 0;
1123 }
1124
1125 if (*remaining > INTMAX_MIN + measured) {
1126 *remaining -= measured;
1127 return -EOVERFLOW;
1128 }
1129
1130 return pldm__msgbuf_set_invalid(remaining);
1131 }
1132
1133 LIBPLDM_CC_NONNULL_ARGS(1)
1134 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_rw_span_string_ascii(struct pldm_msgbuf_rw * ctx,void ** cursor,size_t * length)1135 pldm_msgbuf_rw_span_string_ascii(struct pldm_msgbuf_rw *ctx, void **cursor,
1136 size_t *length)
1137 {
1138 return pldm__msgbuf_span_string_ascii((const uint8_t **)&ctx->cursor,
1139 &ctx->remaining,
1140 (const void **)cursor, length);
1141 }
1142
1143 LIBPLDM_CC_NONNULL_ARGS(1)
1144 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_ro_span_string_ascii(struct pldm_msgbuf_ro * ctx,const void ** cursor,size_t * length)1145 pldm_msgbuf_ro_span_string_ascii(struct pldm_msgbuf_ro *ctx,
1146 const void **cursor, size_t *length)
1147 {
1148 return pldm__msgbuf_span_string_ascii(&ctx->cursor, &ctx->remaining,
1149 cursor, length);
1150 }
1151
1152 LIBPLDM_CC_NONNULL_ARGS(1)
1153 LIBPLDM_CC_ALWAYS_INLINE
1154 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_span_string_utf16(const uint8_t ** buf,intmax_t * remaining,const void ** cursor,size_t * length)1155 int pldm__msgbuf_span_string_utf16(const uint8_t **buf, intmax_t *remaining,
1156 const void **cursor, size_t *length)
1157 {
1158 static const char16_t term = 0;
1159 ptrdiff_t measured;
1160 const void *end;
1161
1162 if (*remaining < 0) {
1163 return pldm__msgbuf_set_invalid(remaining);
1164 }
1165 assert(*buf);
1166
1167 /*
1168 * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do
1169 * not form a UTF16 NUL _code-point_ due to alignment with respect to the
1170 * start of the string
1171 */
1172 end = *buf;
1173 do {
1174 if (end != *buf) {
1175 /*
1176 * If we've looped we've found a relatively-unaligned NUL code-point.
1177 * Scan again from a relatively-aligned start point.
1178 */
1179 end = (char *)end + 1;
1180 }
1181 measured = (char *)end - (char *)*buf;
1182 end = memmem(end, *remaining - measured, &term, sizeof(term));
1183 } while (end && ((uintptr_t)end & 1) != ((uintptr_t)*buf & 1));
1184
1185 if (!end) {
1186 /*
1187 * Optimistically, the last required pattern byte was one beyond the end of
1188 * the buffer. Setting ctx->remaining negative ensures the
1189 * `pldm_msgbuf_complete*()` APIs also return an error.
1190 */
1191 return pldm__msgbuf_set_invalid(remaining);
1192 }
1193
1194 end = (char *)end + sizeof(char16_t);
1195 measured = (char *)end - (char *)*buf;
1196
1197 #if INTMAX_MAX < PTRDIFF_MAX
1198 if (measured >= INTMAX_MAX) {
1199 return pldm__msgbuf_set_invalid(remaining);
1200 }
1201 #endif
1202
1203 if (*remaining >= (intmax_t)measured) {
1204 assert(*buf);
1205 if (cursor) {
1206 *cursor = *buf;
1207 }
1208
1209 *buf += measured;
1210
1211 if (length) {
1212 *length = (size_t)measured;
1213 }
1214
1215 *remaining -= (intmax_t)measured;
1216 return 0;
1217 }
1218
1219 if (*remaining > INTMAX_MIN + (intmax_t)measured) {
1220 *remaining -= (intmax_t)measured;
1221 return -EOVERFLOW;
1222 }
1223
1224 return pldm__msgbuf_set_invalid(remaining);
1225 }
1226
1227 LIBPLDM_CC_NONNULL_ARGS(1)
1228 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_ro_span_string_utf16(struct pldm_msgbuf_ro * ctx,const void ** cursor,size_t * length)1229 pldm_msgbuf_ro_span_string_utf16(struct pldm_msgbuf_ro *ctx,
1230 const void **cursor, size_t *length)
1231 {
1232 return pldm__msgbuf_span_string_utf16(&ctx->cursor, &ctx->remaining,
1233 cursor, length);
1234 }
1235
1236 LIBPLDM_CC_NONNULL_ARGS(1)
1237 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_rw_span_string_utf16(struct pldm_msgbuf_rw * ctx,void ** cursor,size_t * length)1238 pldm_msgbuf_rw_span_string_utf16(struct pldm_msgbuf_rw *ctx, void **cursor,
1239 size_t *length)
1240 {
1241 return pldm__msgbuf_span_string_utf16((const uint8_t **)&ctx->cursor,
1242 &ctx->remaining,
1243 (const void **)cursor, length);
1244 }
1245
1246 LIBPLDM_CC_NONNULL
1247 LIBPLDM_CC_ALWAYS_INLINE int
1248 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_span_remaining(const uint8_t ** buf,intmax_t * remaining,const void ** cursor,size_t * len)1249 pldm__msgbuf_span_remaining(const uint8_t **buf, intmax_t *remaining,
1250 const void **cursor, size_t *len)
1251 {
1252 if (*remaining < 0) {
1253 return -EOVERFLOW;
1254 }
1255
1256 assert(*buf);
1257 *cursor = *buf;
1258 *buf += *remaining;
1259 *len = *remaining;
1260 *remaining = 0;
1261
1262 return 0;
1263 }
1264
1265 LIBPLDM_CC_NONNULL
1266 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_rw_span_remaining(struct pldm_msgbuf_rw * ctx,void ** cursor,size_t * len)1267 pldm_msgbuf_rw_span_remaining(struct pldm_msgbuf_rw *ctx, void **cursor,
1268 size_t *len)
1269 {
1270 return pldm__msgbuf_span_remaining((const uint8_t **)&ctx->cursor,
1271 &ctx->remaining,
1272 (const void **)cursor, len);
1273 }
1274
1275 LIBPLDM_CC_NONNULL
1276 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_ro_span_remaining(struct pldm_msgbuf_ro * ctx,const void ** cursor,size_t * len)1277 pldm_msgbuf_ro_span_remaining(struct pldm_msgbuf_ro *ctx, const void **cursor,
1278 size_t *len)
1279 {
1280 return pldm__msgbuf_span_remaining(&ctx->cursor, &ctx->remaining,
1281 cursor, len);
1282 }
1283
1284 LIBPLDM_CC_NONNULL_ARGS(1)
1285 LIBPLDM_CC_ALWAYS_INLINE
1286 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_span_until(const uint8_t ** buf,intmax_t * remaining,size_t trailer,const void ** cursor,size_t * length)1287 int pldm__msgbuf_span_until(const uint8_t **buf, intmax_t *remaining,
1288 size_t trailer, const void **cursor, size_t *length)
1289 {
1290 #if INTMAX_MAX < SIZE_MAX
1291 if (trailer > INTMAX_MAX) {
1292 return pldm__msgbuf_set_invalid(remaining);
1293 }
1294 #endif
1295
1296 if (*remaining >= (intmax_t)trailer) {
1297 ptrdiff_t delta;
1298
1299 assert(*buf);
1300
1301 delta = *remaining - (intmax_t)trailer;
1302 if (cursor) {
1303 *cursor = *buf;
1304 }
1305 *buf += delta;
1306 if (length) {
1307 *length = delta;
1308 }
1309 *remaining = (intmax_t)trailer;
1310 return 0;
1311 }
1312
1313 if (*remaining > INTMAX_MIN + (intmax_t)trailer) {
1314 *remaining = INTMAX_MIN + (intmax_t)trailer;
1315 return -EOVERFLOW;
1316 }
1317
1318 return pldm__msgbuf_set_invalid(remaining);
1319 }
1320
1321 LIBPLDM_CC_NONNULL_ARGS(1)
1322 LIBPLDM_CC_ALWAYS_INLINE
pldm_msgbuf_ro_span_until(struct pldm_msgbuf_ro * ctx,size_t trailer,const void ** cursor,size_t * length)1323 int pldm_msgbuf_ro_span_until(struct pldm_msgbuf_ro *ctx, size_t trailer,
1324 const void **cursor, size_t *length)
1325 {
1326 return pldm__msgbuf_span_until(&ctx->cursor, &ctx->remaining, trailer,
1327 cursor, length);
1328 }
1329
1330 LIBPLDM_CC_NONNULL_ARGS(1)
1331 LIBPLDM_CC_ALWAYS_INLINE
pldm_msgbuf_rw_span_until(struct pldm_msgbuf_rw * ctx,size_t trailer,void ** cursor,size_t * length)1332 int pldm_msgbuf_rw_span_until(struct pldm_msgbuf_rw *ctx, size_t trailer,
1333 void **cursor, size_t *length)
1334 {
1335 return pldm__msgbuf_span_until((const uint8_t **)&ctx->cursor,
1336 &ctx->remaining, trailer,
1337 (const void **)cursor, length);
1338 }
1339
1340 LIBPLDM_CC_NONNULL
1341 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_peek_remaining(struct pldm_msgbuf_rw * ctx,void ** cursor,size_t * len)1342 pldm_msgbuf_peek_remaining(struct pldm_msgbuf_rw *ctx, void **cursor,
1343 size_t *len)
1344 {
1345 if (ctx->remaining < 0) {
1346 return -EOVERFLOW;
1347 }
1348
1349 assert(ctx->cursor);
1350 *cursor = ctx->cursor;
1351 *len = ctx->remaining;
1352
1353 return 0;
1354 }
1355
1356 LIBPLDM_CC_NONNULL
pldm_msgbuf_skip(struct pldm_msgbuf_rw * ctx,size_t count)1357 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_skip(struct pldm_msgbuf_rw *ctx,
1358 size_t count)
1359 {
1360 #if INTMAX_MAX < SIZE_MAX
1361 if (count > INTMAX_MAX) {
1362 return pldm__msgbuf_rw_invalidate(ctx);
1363 }
1364 #endif
1365
1366 if (ctx->remaining >= (intmax_t)count) {
1367 assert(ctx->cursor);
1368 ctx->cursor += count;
1369 ctx->remaining -= (intmax_t)count;
1370 return 0;
1371 }
1372
1373 if (ctx->remaining > INTMAX_MIN + (intmax_t)count) {
1374 ctx->remaining -= (intmax_t)count;
1375 return -EOVERFLOW;
1376 }
1377
1378 return pldm__msgbuf_rw_invalidate(ctx);
1379 }
1380
1381 /**
1382 * @brief Complete the pldm_msgbuf instance and return the number of bytes
1383 * consumed.
1384 *
1385 * @param ctx - The msgbuf.
1386 * @param orig_len - The original size of the msgbuf, the `len` argument passed to
1387 * pldm_msgbuf_init_errno().
1388 * @param ret_used_len - The number of bytes that have been used from the msgbuf instance.
1389 *
1390 * This can be called after a number of pldm_msgbuf_insert...() calls to
1391 * determine the total size that was written.
1392 *
1393 * @return 0 on success, -EOVERFLOW if an implausible orig_len was provided or
1394 * an out-of-bounds access occurred.
1395 */
1396 LIBPLDM_CC_NONNULL
1397 LIBPLDM_CC_ALWAYS_INLINE
1398 LIBPLDM_CC_WARN_UNUSED_RESULT
pldm_msgbuf_complete_used(struct pldm_msgbuf_rw * ctx,size_t orig_len,size_t * ret_used_len)1399 int pldm_msgbuf_complete_used(struct pldm_msgbuf_rw *ctx, size_t orig_len,
1400 size_t *ret_used_len)
1401 {
1402 int rc;
1403
1404 ctx->cursor = NULL;
1405 rc = pldm_msgbuf_rw_validate(ctx);
1406 if (rc) {
1407 pldm__msgbuf_rw_invalidate(ctx);
1408 return rc;
1409 }
1410
1411 if ((size_t)ctx->remaining > orig_len) {
1412 /* Caller passed incorrect orig_len */
1413 return pldm__msgbuf_rw_invalidate(ctx);
1414 }
1415
1416 *ret_used_len = orig_len - ctx->remaining;
1417 pldm__msgbuf_rw_invalidate(ctx);
1418 return 0;
1419 }
1420
1421 /**
1422 * @brief pldm_msgbuf copy data between two msg buffers
1423 *
1424 * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1425 * @param[in,out] dst - destination of copy from source
1426 * @param[in] size - size of data to be copied
1427 * @param[in] description - description of data copied
1428 *
1429 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1430 * PLDM_ERROR_INVALID_LENGTH otherwise.
1431 * PLDM_ERROR_INVALID_DATA if input is invalid
1432 */
1433 #define pldm_msgbuf_copy(dst, src, type, name) \
1434 pldm__msgbuf_copy(dst, src, sizeof(type), #name)
1435 LIBPLDM_CC_NONNULL
1436 LIBPLDM_CC_ALWAYS_INLINE int
1437 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_copy(struct pldm_msgbuf_rw * dst,struct pldm_msgbuf_ro * src,size_t size,const char * description LIBPLDM_CC_UNUSED)1438 pldm__msgbuf_copy(struct pldm_msgbuf_rw *dst, struct pldm_msgbuf_ro *src,
1439 size_t size, const char *description LIBPLDM_CC_UNUSED)
1440 {
1441 #if INTMAX_MAX < SIZE_MAX
1442 if (size > INTMAX_MAX) {
1443 pldm__msgbuf_ro_invalidate(src);
1444 pldm__msgbuf_rw_invalidate(dst);
1445 return -EOVERFLOW;
1446 }
1447 #endif
1448
1449 if (src->remaining >= (intmax_t)size &&
1450 dst->remaining >= (intmax_t)size) {
1451 assert(src->cursor && dst->cursor);
1452 memcpy(dst->cursor, src->cursor, size);
1453 src->cursor += size;
1454 src->remaining -= (intmax_t)size;
1455 dst->cursor += size;
1456 dst->remaining -= (intmax_t)size;
1457 return 0;
1458 }
1459
1460 if (src->remaining > INTMAX_MIN + (intmax_t)size) {
1461 src->remaining -= (intmax_t)size;
1462 } else {
1463 pldm__msgbuf_ro_invalidate(src);
1464 }
1465
1466 if (dst->remaining > INTMAX_MIN + (intmax_t)size) {
1467 dst->remaining -= (intmax_t)size;
1468 } else {
1469 pldm__msgbuf_rw_invalidate(dst);
1470 }
1471
1472 return -EOVERFLOW;
1473 }
1474
1475 LIBPLDM_CC_NONNULL
1476 LIBPLDM_CC_WARN_UNUSED_RESULT
1477 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf_rw * dst,struct pldm_msgbuf_ro * src)1478 pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf_rw *dst,
1479 struct pldm_msgbuf_ro *src)
1480 {
1481 const void *ascii = NULL;
1482 size_t len = 0;
1483 int rc;
1484
1485 rc = pldm_msgbuf_ro_span_string_ascii(src, &ascii, &len);
1486 if (rc < 0) {
1487 return rc;
1488 }
1489
1490 return pldm__msgbuf_insert_array_void(dst, len, ascii, len);
1491 }
1492
1493 LIBPLDM_CC_NONNULL
1494 LIBPLDM_CC_WARN_UNUSED_RESULT
1495 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf_rw * dst,struct pldm_msgbuf_ro * src)1496 pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf_rw *dst,
1497 struct pldm_msgbuf_ro *src)
1498 {
1499 const void *utf16 = NULL;
1500 size_t len = 0;
1501 int rc;
1502
1503 rc = pldm_msgbuf_ro_span_string_utf16(src, &utf16, &len);
1504 if (rc < 0) {
1505 return rc;
1506 }
1507
1508 return pldm__msgbuf_insert_array_void(dst, len, utf16, len);
1509 }
1510
1511 /**
1512 * @brief pldm_msgbuf uint8_t extractor for a size_t
1513 *
1514 * @param[in,out] ctx - pldm_msgbuf context for extractor
1515 * @param[out] dst - destination of extracted value
1516 *
1517 * @return 0 if buffer accesses were in-bounds,
1518 * -EINVAL if dst pointer is invalid,
1519 * -EOVERFLOW is the buffer was out of bound.
1520 */
1521 #define pldm_msgbuf_extract_uint8_to_size(ctx, dst) \
1522 pldm__msgbuf_extract_uint8_to_size(ctx, &(dst))
1523 LIBPLDM_CC_NONNULL
1524 LIBPLDM_CC_ALWAYS_INLINE int
1525 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint8_to_size(struct pldm_msgbuf_ro * ctx,size_t * dst)1526 pldm__msgbuf_extract_uint8_to_size(struct pldm_msgbuf_ro *ctx, size_t *dst)
1527 {
1528 uint8_t value = 0;
1529 int rc;
1530
1531 rc = pldm__msgbuf_extract_uint8(ctx, &value);
1532 if (rc) {
1533 return rc;
1534 }
1535
1536 static_assert(SIZE_MAX >= UINT8_MAX, "Invalid promotion");
1537
1538 *dst = value;
1539 return 0;
1540 }
1541
1542 /**
1543 * @brief pldm_msgbuf uint16_t extractor for a size_t
1544 *
1545 * @param[in,out] ctx - pldm_msgbuf context for extractor
1546 * @param[out] dst - destination of extracted value
1547 *
1548 * @return 0 if buffer accesses were in-bounds,
1549 * -EINVAL if dst pointer is invalid,
1550 * -EOVERFLOW is the buffer was out of bound.
1551 */
1552 #define pldm_msgbuf_extract_uint16_to_size(ctx, dst) \
1553 pldm__msgbuf_extract_uint16_to_size(ctx, &(dst))
1554 LIBPLDM_CC_NONNULL
1555 LIBPLDM_CC_ALWAYS_INLINE int
1556 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint16_to_size(struct pldm_msgbuf_ro * ctx,size_t * dst)1557 pldm__msgbuf_extract_uint16_to_size(struct pldm_msgbuf_ro *ctx, size_t *dst)
1558 {
1559 uint16_t value = 0;
1560 int rc;
1561
1562 rc = pldm__msgbuf_extract_uint16(ctx, &value);
1563 if (rc) {
1564 return rc;
1565 }
1566
1567 static_assert(SIZE_MAX >= UINT16_MAX, "Invalid promotion");
1568
1569 *dst = value;
1570 return 0;
1571 }
1572
1573 /**
1574 * @brief pldm_msgbuf uint32_t extractor for a size_t
1575 *
1576 * @param[in,out] ctx - pldm_msgbuf context for extractor
1577 * @param[out] dst - destination of extracted value
1578 *
1579 * @return 0 if buffer accesses were in-bounds,
1580 * -EINVAL if dst pointer is invalid,
1581 * -EOVERFLOW is the buffer was out of bound.
1582 */
1583 #define pldm_msgbuf_extract_uint32_to_size(ctx, dst) \
1584 pldm__msgbuf_extract_uint32_to_size(ctx, &(dst))
1585 LIBPLDM_CC_NONNULL
1586 LIBPLDM_CC_ALWAYS_INLINE int
1587 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint32_to_size(struct pldm_msgbuf_ro * ctx,size_t * dst)1588 pldm__msgbuf_extract_uint32_to_size(struct pldm_msgbuf_ro *ctx, size_t *dst)
1589 {
1590 uint32_t value = 0;
1591 int rc;
1592
1593 rc = pldm__msgbuf_extract_uint32(ctx, &value);
1594 if (rc) {
1595 return rc;
1596 }
1597
1598 static_assert(SIZE_MAX >= UINT32_MAX, "Invalid promotion");
1599
1600 *dst = value;
1601 return 0;
1602 }
1603
1604 #ifdef __cplusplus
1605 }
1606 #endif
1607
1608 #endif
1609