1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 #ifndef PLDM_MSGBUF_H
3 #define PLDM_MSGBUF_H
4
5 /*
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 extern "C" {
40 #endif
41
42 #include <libpldm/base.h>
43 #include <libpldm/pldm_types.h>
44
45 #include "compiler.h"
46
47 #include <assert.h>
48 #include <endian.h>
49 #include <errno.h>
50 #include <limits.h>
51 #include <stdbool.h>
52 #include <stdint.h>
53 #include <string.h>
54 #include <sys/types.h>
55
56 /*
57 * We can't use static_assert() outside of some other C construct. Deal
58 * with high-level global assertions by burying them in an unused struct
59 * declaration, that has a sole member for compliance with the requirement that
60 * types must have a size.
61 */
62 static struct {
63 static_assert(
64 INTMAX_MAX != SIZE_MAX,
65 "Extraction and insertion value comparisons may be broken");
66 static_assert(INTMAX_MIN + INTMAX_MAX <= 0,
67 "Extraction and insertion arithmetic may be broken");
68 static_assert(PLDM_SUCCESS == 0, "Error handling is broken");
69 int compliance;
70 } build_assertions __attribute__((unused));
71
72 enum pldm_msgbuf_error_mode {
73 PLDM_MSGBUF_PLDM_CC = 0x5a,
74 PLDM_MSGBUF_C_ERRNO = 0xa5,
75 };
76
77 struct pldm_msgbuf {
78 uint8_t *cursor;
79 intmax_t remaining;
80 enum pldm_msgbuf_error_mode mode;
81 };
82
83 /**
84 * @brief Either negate an errno value or return a value mapped to a PLDM
85 * completion code.
86 *
87 * Note that `pldm_msgbuf_status()` is purely internal to the msgbuf API
88 * for ergonomics. It's preferred that we don't try to unify this with
89 * `pldm_xlate_errno()` from src/api.h despite the similarities.
90 *
91 * @param[in] ctx - The msgbuf context providing the personality info
92 * @param[in] err - The positive errno value to translate
93 *
94 * @return Either the negated value of @p err if the context's error mode is
95 * `PLDM_MSGBUF_C_ERRNO`, or the equivalent PLDM completion code if the
96 * error mode is `PLDM_MSGBUF_PLDM_CC`.
97 */
98 __attribute__((always_inline)) static inline int
pldm_msgbuf_status(struct pldm_msgbuf * ctx,unsigned int err)99 pldm_msgbuf_status(struct pldm_msgbuf *ctx, unsigned int err)
100 {
101 int rc;
102
103 assert(err != 0);
104 assert(err <= INT_MAX);
105
106 if (ctx->mode == PLDM_MSGBUF_C_ERRNO) {
107 if (err > INT_MAX) {
108 return -EINVAL;
109 }
110
111 static_assert(INT_MIN + INT_MAX < 0,
112 "Arithmetic assumption failure");
113 return -((int)err);
114 }
115
116 if (err > INT_MAX) {
117 return PLDM_ERROR;
118 }
119
120 assert(ctx->mode == PLDM_MSGBUF_PLDM_CC);
121 switch (err) {
122 case EINVAL:
123 rc = PLDM_ERROR_INVALID_DATA;
124 break;
125 case EBADMSG:
126 case EOVERFLOW:
127 rc = PLDM_ERROR_INVALID_LENGTH;
128 break;
129 default:
130 assert(false);
131 rc = PLDM_ERROR;
132 break;
133 }
134
135 assert(rc > 0);
136 return rc;
137 }
138
139 /**
140 * @brief Initialize pldm buf struct for buf extractor
141 *
142 * @param[out] ctx - pldm_msgbuf context for extractor
143 * @param[in] minsize - The minimum required length of buffer `buf`
144 * @param[in] buf - buffer to be extracted
145 * @param[in] len - size of buffer
146 *
147 * @return 0 on success, otherwise an error code appropriate for the current
148 * personality.
149 */
150 __attribute__((always_inline)) static inline int
151 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_init(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)152 pldm__msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
153 size_t len)
154 {
155 assert(ctx);
156 assert(ctx->mode == PLDM_MSGBUF_PLDM_CC ||
157 ctx->mode == PLDM_MSGBUF_C_ERRNO);
158
159 if (!buf) {
160 return pldm_msgbuf_status(ctx, EINVAL);
161 }
162
163 if ((minsize > len)) {
164 return pldm_msgbuf_status(ctx, EOVERFLOW);
165 }
166
167 #if INTMAX_MAX < SIZE_MAX
168 if (len > INTMAX_MAX) {
169 return pldm_msgbuf_status(ctx, EOVERFLOW);
170 }
171 #endif
172
173 if ((uintptr_t)buf + len < len) {
174 return pldm_msgbuf_status(ctx, EOVERFLOW);
175 }
176
177 ctx->cursor = (uint8_t *)buf;
178 ctx->remaining = (intmax_t)len;
179
180 return 0;
181 }
182
183 /**
184 * @brief Initialise a msgbuf instance to return errors as PLDM completion codes
185 *
186 * @see pldm__msgbuf_init
187 *
188 * @param[out] ctx - pldm_msgbuf context for extractor
189 * @param[in] minsize - The minimum required length of buffer `buf`
190 * @param[in] buf - buffer to be extracted
191 * @param[in] len - size of buffer
192 *
193 * @return PLDM_SUCCESS if the provided buffer region is sensible,
194 * otherwise PLDM_ERROR_INVALID_DATA if pointer parameters are invalid,
195 * or PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
196 */
197 __attribute__((always_inline)) static inline int
pldm_msgbuf_init_cc(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)198 pldm_msgbuf_init_cc(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
199 size_t len)
200 {
201 if (!ctx) {
202 return PLDM_ERROR_INVALID_DATA;
203 }
204
205 ctx->mode = PLDM_MSGBUF_PLDM_CC;
206 return pldm__msgbuf_init(ctx, minsize, buf, len);
207 }
208
209 /**
210 * @brief Initialise a msgbuf instance to return errors as negative errno values
211 *
212 * @see pldm__msgbuf_init
213 *
214 * @param[out] ctx - pldm_msgbuf context for extractor
215 * @param[in] minsize - The minimum required length of buffer `buf`
216 * @param[in] buf - buffer to be extracted
217 * @param[in] len - size of buffer
218 *
219 * @return 0 if the provided buffer region is sensible, otherwise -EINVAL if
220 * pointer parameters are invalid, or -EOVERFLOW if length constraints
221 * are violated.
222 */
223 __attribute__((always_inline)) static inline int
pldm_msgbuf_init_errno(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)224 pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
225 size_t len)
226 {
227 if (!ctx) {
228 return -EINVAL;
229 }
230
231 ctx->mode = PLDM_MSGBUF_C_ERRNO;
232 return pldm__msgbuf_init(ctx, minsize, buf, len);
233 }
234
235 /**
236 * @brief Validate buffer overflow state
237 *
238 * @param[in] ctx - pldm_msgbuf context for extractor
239 *
240 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
241 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
242 * prior accesses would have occurred beyond the bounds of the buffer, and
243 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
244 * pointer.
245 */
246 __attribute__((always_inline)) static inline int
pldm_msgbuf_validate(struct pldm_msgbuf * ctx)247 pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
248 {
249 assert(ctx);
250 if (ctx->remaining < 0) {
251 return pldm_msgbuf_status(ctx, EOVERFLOW);
252 }
253
254 return 0;
255 }
256
257 /**
258 * @brief Test whether a message buffer has been exactly consumed
259 *
260 * @param[in] ctx - pldm_msgbuf context for extractor
261 *
262 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
263 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
264 * indicates that an incorrect sequence of accesses have occurred, and
265 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
266 * pointer.
267 */
268 __attribute__((always_inline)) static inline int
pldm_msgbuf_consumed(struct pldm_msgbuf * ctx)269 pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
270 {
271 assert(ctx);
272 if (ctx->remaining != 0) {
273 return pldm_msgbuf_status(ctx, EBADMSG);
274 }
275
276 return 0;
277 }
278
279 /**
280 * @brief Destroy the pldm buf
281 *
282 * @param[in] ctx - pldm_msgbuf context for extractor
283 *
284 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
285 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
286 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
287 * bounds of the buffer.
288 */
289 __attribute__((always_inline)) static inline int
pldm_msgbuf_destroy(struct pldm_msgbuf * ctx)290 pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
291 {
292 int valid;
293
294 assert(ctx);
295 valid = pldm_msgbuf_validate(ctx);
296
297 ctx->cursor = NULL;
298 ctx->remaining = 0;
299
300 return valid;
301 }
302
303 /**
304 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
305 * has been completely consumed without overflow
306 *
307 * @param[in] ctx - pldm_msgbuf context
308 *
309 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
310 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
311 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
312 * have occurred byond the bounds of the buffer
313 */
314 __attribute__((always_inline)) static inline int
pldm_msgbuf_destroy_consumed(struct pldm_msgbuf * ctx)315 pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
316 {
317 int consumed;
318
319 assert(ctx);
320 consumed = pldm_msgbuf_consumed(ctx);
321
322 ctx->cursor = NULL;
323 ctx->remaining = 0;
324
325 return consumed;
326 }
327
328 /*
329 * Exploit the pre-processor to perform type checking by macro substitution.
330 *
331 * A C type is defined by its alignment as well as its object
332 * size, and compilers have a hammer to enforce it in the form of
333 * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
334 * the libpldm public API this presents a problem: Naively attempting to use the
335 * msgbuf APIs on a member of a packed struct would yield an error.
336 *
337 * The msgbuf APIs are implemented such that data is moved through unaligned
338 * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
339 * make the object pointers take a trip through `void *` at its API boundary.
340 * That presents a bit too much of an opportunity to non-surgically remove your
341 * own foot, so here we set about doing something to mitigate that as well.
342 *
343 * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
344 * only for the purpose of object sizes, disregarding alignment. We have a few
345 * constraints that cause some headaches:
346 *
347 * 1. We have to perform the type-check before a call through a C function,
348 * as the function must take the object pointer argument as `void *`.
349 * Essentially, this constrains us to doing something with macros.
350 *
351 * 2. While libpldm is a C library, its test suite is written in C++ to take
352 * advantage of gtest.
353 *
354 * 3. Ideally we'd do something with C's `static_assert()`, however
355 * `static_assert()` is defined as void, and as we're constrained to macros,
356 * using `static_assert()` would require a statement-expression
357 *
358 * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
359 * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
360 * the purpose of enabling statement-expressions in this one instance.
361 *
362 * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
363 * however it's implemented in terms of `_Generic()`, which is not available
364 * in C++.
365 *
366 * Combined this means we need separate solutions for C and C++.
367 *
368 * For C, as we don't have statement-expressions, we need to exploit some other
369 * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
370 * API function call. We also have to take care of the fact that the call-sites
371 * may be in the context of a variable assignment for error-handling purposes.
372 * The key observation is that we can use the comma operator as a sequence point
373 * to order the type check before the API call, discarding the "result" value of
374 * the type check and yielding the return value of the API call.
375 *
376 * C++ could be less of a headache than the C as we can leverage template
377 * functions. An advantage of template functions is that while their definition
378 * is driven by instantion, the definition does not appear at the source
379 * location of the instantiation, which gives it a great leg-up over the problems
380 * we have in the C path. However, the use of the msgbuf APIs in the test suite
381 * still makes things somewhat tricky, as the call-sites in the test suite are
382 * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
383 * takes both the object type and the required type as template arguments, and
384 * then define the object pointer parameter as `void *` for a call through to
385 * the appropriate msgbuf API. However, because the msgbuf API call-sites are
386 * encapsulated in gtest macros, use of commas in the template specification
387 * causes pre-processor confusion. In this way we're constrained to only one
388 * template argument per function.
389 *
390 * Implement the C++ path using template functions that take the destination
391 * object type as a template argument, while the name of the function symbols
392 * are derived from the required type. The manual implementations of these
393 * appear at the end of the header. The type safety is actually enforced
394 * by `static_assert()` this time, as we can use statements as we're not
395 * constrained to an expression in the templated function body.
396 *
397 * The invocations of pldm_msgbuf_extract_typecheck() typically result in
398 * double-evaluation of some arguments. We're not yet bothered by this for two
399 * reasons:
400 *
401 * 1. The nature of the current call-sites are such that there are no
402 * argument expressions that result in undesirable side-effects
403 *
404 * 2. It's an API internal to the libpldm implementation, and we can fix things
405 * whenever something crops up the violates the observation in 1.
406 */
407 #ifdef __cplusplus
408 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
409 pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
410 #else
411 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
412 (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
413 #endif
414
415 /**
416 * @brief pldm_msgbuf extractor for a uint8_t
417 *
418 * @param[in,out] ctx - pldm_msgbuf context for extractor
419 * @param[out] dst - destination of extracted value
420 *
421 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
422 * PLDM_ERROR_INVALID_LENGTH otherwise.
423 * PLDM_ERROR_INVALID_DATA if input a invalid ctx
424 */
425 #define pldm_msgbuf_extract_uint8(ctx, dst) \
426 pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \
427 dst, ctx, dst)
428 __attribute__((always_inline)) static inline int
429 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint8(struct pldm_msgbuf * ctx,void * dst)430 pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
431 {
432 assert(ctx);
433
434 if (!ctx->cursor || !dst) {
435 return pldm_msgbuf_status(ctx, EINVAL);
436 }
437
438 if (ctx->remaining == INTMAX_MIN) {
439 assert(ctx->remaining < 0);
440 return pldm_msgbuf_status(ctx, EOVERFLOW);
441 }
442 ctx->remaining -= sizeof(uint8_t);
443 assert(ctx->remaining >= 0);
444 if (ctx->remaining < 0) {
445 return pldm_msgbuf_status(ctx, EOVERFLOW);
446 }
447
448 memcpy(dst, ctx->cursor, sizeof(uint8_t));
449
450 ctx->cursor++;
451 return 0;
452 }
453
454 #define pldm_msgbuf_extract_int8(ctx, dst) \
455 pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \
456 ctx, dst)
457 __attribute__((always_inline)) static inline int
458 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int8(struct pldm_msgbuf * ctx,void * dst)459 pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
460 {
461 assert(ctx);
462
463 if (!ctx->cursor || !dst) {
464 return pldm_msgbuf_status(ctx, EINVAL);
465 }
466
467 if (ctx->remaining == INTMAX_MIN) {
468 assert(ctx->remaining < 0);
469 return pldm_msgbuf_status(ctx, EOVERFLOW);
470 }
471 ctx->remaining -= sizeof(int8_t);
472 assert(ctx->remaining >= 0);
473 if (ctx->remaining < 0) {
474 return pldm_msgbuf_status(ctx, EOVERFLOW);
475 }
476
477 memcpy(dst, ctx->cursor, sizeof(int8_t));
478 ctx->cursor++;
479 return 0;
480 }
481
482 #define pldm_msgbuf_extract_uint16(ctx, dst) \
483 pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \
484 dst, ctx, dst)
485 __attribute__((always_inline)) static inline int
486 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint16(struct pldm_msgbuf * ctx,void * dst)487 pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst)
488 {
489 uint16_t ldst;
490
491 assert(ctx);
492
493 if (!ctx->cursor || !dst) {
494 return pldm_msgbuf_status(ctx, EINVAL);
495 }
496
497 // Check for underflow while tracking the magnitude of the buffer overflow
498 static_assert(
499 // NOLINTNEXTLINE(bugprone-sizeof-expression)
500 sizeof(ldst) < INTMAX_MAX,
501 "The following addition may not uphold the runtime assertion");
502 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
503 assert(ctx->remaining < 0);
504 return pldm_msgbuf_status(ctx, EOVERFLOW);
505 }
506
507 // Check for buffer overflow. If we overflow, account for the request as
508 // negative values in ctx->remaining. This way we can debug how far
509 // we've overflowed.
510 ctx->remaining -= sizeof(ldst);
511
512 // Prevent the access if it would overflow. First, assert so we blow up
513 // the test suite right at the point of failure. However, cater to
514 // -DNDEBUG by explicitly testing that the access is valid.
515 assert(ctx->remaining >= 0);
516 if (ctx->remaining < 0) {
517 return pldm_msgbuf_status(ctx, EOVERFLOW);
518 }
519
520 // Use memcpy() to have the compiler deal with any alignment
521 // issues on the target architecture
522 memcpy(&ldst, ctx->cursor, sizeof(ldst));
523
524 // Only assign the target value once it's correctly decoded
525 ldst = le16toh(ldst);
526
527 // Allow storing to unaligned
528 memcpy(dst, &ldst, sizeof(ldst));
529
530 ctx->cursor += sizeof(ldst);
531
532 return 0;
533 }
534
535 #define pldm_msgbuf_extract_int16(ctx, dst) \
536 pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \
537 dst, ctx, dst)
538 __attribute__((always_inline)) static inline int
539 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int16(struct pldm_msgbuf * ctx,void * dst)540 pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
541 {
542 int16_t ldst;
543
544 assert(ctx);
545
546 if (!ctx->cursor || !dst) {
547 return pldm_msgbuf_status(ctx, EINVAL);
548 }
549
550 static_assert(
551 // NOLINTNEXTLINE(bugprone-sizeof-expression)
552 sizeof(ldst) < INTMAX_MAX,
553 "The following addition may not uphold the runtime assertion");
554 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
555 assert(ctx->remaining < 0);
556 return pldm_msgbuf_status(ctx, EOVERFLOW);
557 }
558 ctx->remaining -= sizeof(ldst);
559 assert(ctx->remaining >= 0);
560 if (ctx->remaining < 0) {
561 return pldm_msgbuf_status(ctx, EOVERFLOW);
562 }
563
564 memcpy(&ldst, ctx->cursor, sizeof(ldst));
565
566 ldst = le16toh(ldst);
567 memcpy(dst, &ldst, sizeof(ldst));
568 ctx->cursor += sizeof(ldst);
569
570 return 0;
571 }
572
573 #define pldm_msgbuf_extract_uint32(ctx, dst) \
574 pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \
575 dst, ctx, dst)
576 __attribute__((always_inline)) static inline int
577 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint32(struct pldm_msgbuf * ctx,void * dst)578 pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst)
579 {
580 uint32_t ldst;
581
582 assert(ctx);
583
584 if (!ctx->cursor || !dst) {
585 return pldm_msgbuf_status(ctx, EINVAL);
586 }
587
588 static_assert(
589 // NOLINTNEXTLINE(bugprone-sizeof-expression)
590 sizeof(ldst) < INTMAX_MAX,
591 "The following addition may not uphold the runtime assertion");
592 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
593 assert(ctx->remaining < 0);
594 return pldm_msgbuf_status(ctx, EOVERFLOW);
595 }
596 ctx->remaining -= sizeof(ldst);
597 assert(ctx->remaining >= 0);
598 if (ctx->remaining < 0) {
599 return pldm_msgbuf_status(ctx, EOVERFLOW);
600 }
601
602 memcpy(&ldst, ctx->cursor, sizeof(ldst));
603 ldst = le32toh(ldst);
604 memcpy(dst, &ldst, sizeof(ldst));
605 ctx->cursor += sizeof(ldst);
606
607 return 0;
608 }
609
610 #define pldm_msgbuf_extract_int32(ctx, dst) \
611 pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \
612 dst, ctx, dst)
613 __attribute__((always_inline)) static inline int
614 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int32(struct pldm_msgbuf * ctx,void * dst)615 pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
616 {
617 int32_t ldst;
618
619 assert(ctx);
620
621 if (!ctx->cursor || !dst) {
622 return pldm_msgbuf_status(ctx, EINVAL);
623 }
624
625 static_assert(
626 // NOLINTNEXTLINE(bugprone-sizeof-expression)
627 sizeof(ldst) < INTMAX_MAX,
628 "The following addition may not uphold the runtime assertion");
629 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
630 assert(ctx->remaining < 0);
631 return pldm_msgbuf_status(ctx, EOVERFLOW);
632 }
633 ctx->remaining -= sizeof(ldst);
634 assert(ctx->remaining >= 0);
635 if (ctx->remaining < 0) {
636 return pldm_msgbuf_status(ctx, EOVERFLOW);
637 }
638
639 memcpy(&ldst, ctx->cursor, sizeof(ldst));
640 ldst = le32toh(ldst);
641 memcpy(dst, &ldst, sizeof(ldst));
642 ctx->cursor += sizeof(ldst);
643
644 return PLDM_SUCCESS;
645 }
646
647 #define pldm_msgbuf_extract_real32(ctx, dst) \
648 pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \
649 dst, ctx, dst)
650 __attribute__((always_inline)) static inline int
651 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_real32(struct pldm_msgbuf * ctx,void * dst)652 pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst)
653 {
654 uint32_t ldst;
655
656 static_assert(sizeof(real32_t) == sizeof(ldst),
657 "Mismatched type sizes for dst and ldst");
658
659 assert(ctx);
660
661 if (!ctx->cursor || !dst) {
662 return pldm_msgbuf_status(ctx, EINVAL);
663 }
664
665 static_assert(
666 // NOLINTNEXTLINE(bugprone-sizeof-expression)
667 sizeof(ldst) < INTMAX_MAX,
668 "The following addition may not uphold the runtime assertion");
669 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
670 assert(ctx->remaining < 0);
671 return pldm_msgbuf_status(ctx, EOVERFLOW);
672 }
673 ctx->remaining -= sizeof(ldst);
674 assert(ctx->remaining >= 0);
675 if (ctx->remaining < 0) {
676 return pldm_msgbuf_status(ctx, EOVERFLOW);
677 }
678
679 memcpy(&ldst, ctx->cursor, sizeof(ldst));
680 ldst = le32toh(ldst);
681 memcpy(dst, &ldst, sizeof(ldst));
682 ctx->cursor += sizeof(ldst);
683
684 return 0;
685 }
686
687 /**
688 * Extract the field at the msgbuf cursor into the lvalue named by dst.
689 *
690 * @param ctx The msgbuf context object
691 * @param dst The lvalue into which the field at the msgbuf cursor should be
692 * extracted
693 *
694 * @return PLDM_SUCCESS on success, otherwise another value on error
695 */
696 #define pldm_msgbuf_extract(ctx, dst) \
697 _Generic((dst), \
698 uint8_t: pldm__msgbuf_extract_uint8, \
699 int8_t: pldm__msgbuf_extract_int8, \
700 uint16_t: pldm__msgbuf_extract_uint16, \
701 int16_t: pldm__msgbuf_extract_int16, \
702 uint32_t: pldm__msgbuf_extract_uint32, \
703 int32_t: pldm__msgbuf_extract_int32, \
704 real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
705
706 /**
707 * Extract the field at the msgbuf cursor into the object pointed-to by dst.
708 *
709 * @param ctx The msgbuf context object
710 * @param dst The pointer to the object into which the field at the msgbuf
711 * cursor should be extracted
712 *
713 * @return PLDM_SUCCESS on success, otherwise another value on error
714 */
715 #define pldm_msgbuf_extract_p(ctx, dst) \
716 _Generic((dst), \
717 uint8_t *: pldm__msgbuf_extract_uint8, \
718 int8_t *: pldm__msgbuf_extract_int8, \
719 uint16_t *: pldm__msgbuf_extract_uint16, \
720 int16_t *: pldm__msgbuf_extract_int16, \
721 uint32_t *: pldm__msgbuf_extract_uint32, \
722 int32_t *: pldm__msgbuf_extract_int32, \
723 real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
724
725 __attribute__((always_inline)) static inline int
pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf * ctx,uint8_t * dst,size_t count)726 pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, uint8_t *dst,
727 size_t count)
728 {
729 assert(ctx);
730
731 if (!ctx->cursor || !dst) {
732 return pldm_msgbuf_status(ctx, EINVAL);
733 }
734
735 if (!count) {
736 return 0;
737 }
738
739 #if INTMAX_MAX < SIZE_MAX
740 if (count > INTMAX_MAX) {
741 return pldm_msgbuf_status(ctx, EOVERFLOW);
742 }
743 #endif
744
745 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
746 return pldm_msgbuf_status(ctx, EOVERFLOW);
747 }
748 ctx->remaining -= (intmax_t)count;
749 assert(ctx->remaining >= 0);
750 if (ctx->remaining < 0) {
751 return pldm_msgbuf_status(ctx, EOVERFLOW);
752 }
753
754 memcpy(dst, ctx->cursor, count);
755 ctx->cursor += count;
756
757 return 0;
758 }
759
760 #define pldm_msgbuf_extract_array(ctx, dst, count) \
761 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
762 count)
763
764 __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_uint32(struct pldm_msgbuf * ctx,const uint32_t src)765 pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, const uint32_t src)
766 {
767 uint32_t val = htole32(src);
768
769 assert(ctx);
770
771 if (!ctx->cursor) {
772 return pldm_msgbuf_status(ctx, EINVAL);
773 }
774
775 static_assert(
776 // NOLINTNEXTLINE(bugprone-sizeof-expression)
777 sizeof(src) < INTMAX_MAX,
778 "The following addition may not uphold the runtime assertion");
779 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
780 assert(ctx->remaining < 0);
781 return pldm_msgbuf_status(ctx, EOVERFLOW);
782 }
783 ctx->remaining -= sizeof(src);
784 assert(ctx->remaining >= 0);
785 if (ctx->remaining < 0) {
786 return pldm_msgbuf_status(ctx, EOVERFLOW);
787 }
788
789 memcpy(ctx->cursor, &val, sizeof(val));
790 ctx->cursor += sizeof(src);
791
792 return 0;
793 }
794
795 __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_uint16(struct pldm_msgbuf * ctx,const uint16_t src)796 pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, const uint16_t src)
797 {
798 uint16_t val = htole16(src);
799
800 assert(ctx);
801
802 if (!ctx->cursor) {
803 return pldm_msgbuf_status(ctx, EINVAL);
804 }
805
806 static_assert(
807 // NOLINTNEXTLINE(bugprone-sizeof-expression)
808 sizeof(src) < INTMAX_MAX,
809 "The following addition may not uphold the runtime assertion");
810 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
811 assert(ctx->remaining < 0);
812 return pldm_msgbuf_status(ctx, EOVERFLOW);
813 }
814 ctx->remaining -= sizeof(src);
815 assert(ctx->remaining >= 0);
816 if (ctx->remaining < 0) {
817 return pldm_msgbuf_status(ctx, EOVERFLOW);
818 }
819
820 memcpy(ctx->cursor, &val, sizeof(val));
821 ctx->cursor += sizeof(src);
822
823 return 0;
824 }
825
826 __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_uint8(struct pldm_msgbuf * ctx,const uint8_t src)827 pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, const uint8_t src)
828 {
829 assert(ctx);
830
831 if (!ctx->cursor) {
832 return pldm_msgbuf_status(ctx, EINVAL);
833 }
834
835 static_assert(
836 // NOLINTNEXTLINE(bugprone-sizeof-expression)
837 sizeof(src) < INTMAX_MAX,
838 "The following addition may not uphold the runtime assertion");
839 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
840 assert(ctx->remaining < 0);
841 return pldm_msgbuf_status(ctx, EOVERFLOW);
842 }
843 ctx->remaining -= sizeof(src);
844 assert(ctx->remaining >= 0);
845 if (ctx->remaining < 0) {
846 return pldm_msgbuf_status(ctx, EOVERFLOW);
847 }
848
849 memcpy(ctx->cursor, &src, sizeof(src));
850 ctx->cursor += sizeof(src);
851
852 return 0;
853 }
854
855 __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_int32(struct pldm_msgbuf * ctx,const int32_t src)856 pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, const int32_t src)
857 {
858 int32_t val = htole32(src);
859
860 assert(ctx);
861
862 if (!ctx->cursor) {
863 return pldm_msgbuf_status(ctx, EINVAL);
864 }
865
866 static_assert(
867 // NOLINTNEXTLINE(bugprone-sizeof-expression)
868 sizeof(src) < INTMAX_MAX,
869 "The following addition may not uphold the runtime assertion");
870 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
871 assert(ctx->remaining < 0);
872 return pldm_msgbuf_status(ctx, EOVERFLOW);
873 }
874 ctx->remaining -= sizeof(src);
875 assert(ctx->remaining >= 0);
876 if (ctx->remaining < 0) {
877 return pldm_msgbuf_status(ctx, EOVERFLOW);
878 }
879
880 memcpy(ctx->cursor, &val, sizeof(val));
881 ctx->cursor += sizeof(src);
882
883 return 0;
884 }
885
886 __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_int16(struct pldm_msgbuf * ctx,const int16_t src)887 pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, const int16_t src)
888 {
889 int16_t val = htole16(src);
890
891 assert(ctx);
892
893 if (!ctx->cursor) {
894 return pldm_msgbuf_status(ctx, EINVAL);
895 }
896
897 static_assert(
898 // NOLINTNEXTLINE(bugprone-sizeof-expression)
899 sizeof(src) < INTMAX_MAX,
900 "The following addition may not uphold the runtime assertion");
901 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
902 assert(ctx->remaining < 0);
903 return pldm_msgbuf_status(ctx, EOVERFLOW);
904 }
905 ctx->remaining -= sizeof(src);
906 assert(ctx->remaining >= 0);
907 if (ctx->remaining < 0) {
908 return pldm_msgbuf_status(ctx, EOVERFLOW);
909 }
910
911 memcpy(ctx->cursor, &val, sizeof(val));
912 ctx->cursor += sizeof(src);
913
914 return 0;
915 }
916
917 __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_int8(struct pldm_msgbuf * ctx,const int8_t src)918 pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, const int8_t src)
919 {
920 assert(ctx);
921
922 if (!ctx->cursor) {
923 return pldm_msgbuf_status(ctx, EINVAL);
924 }
925
926 static_assert(
927 // NOLINTNEXTLINE(bugprone-sizeof-expression)
928 sizeof(src) < INTMAX_MAX,
929 "The following addition may not uphold the runtime assertion");
930 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
931 assert(ctx->remaining < 0);
932 return pldm_msgbuf_status(ctx, EOVERFLOW);
933 }
934 ctx->remaining -= sizeof(src);
935 assert(ctx->remaining >= 0);
936 if (ctx->remaining < 0) {
937 return pldm_msgbuf_status(ctx, EOVERFLOW);
938 }
939
940 memcpy(ctx->cursor, &src, sizeof(src));
941 ctx->cursor += sizeof(src);
942
943 return 0;
944 }
945
946 #define pldm_msgbuf_insert(dst, src) \
947 _Generic((src), \
948 uint8_t: pldm_msgbuf_insert_uint8, \
949 int8_t: pldm_msgbuf_insert_int8, \
950 uint16_t: pldm_msgbuf_insert_uint16, \
951 int16_t: pldm_msgbuf_insert_int16, \
952 uint32_t: pldm_msgbuf_insert_uint32, \
953 int32_t: pldm_msgbuf_insert_int32)(dst, src)
954
955 __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf * ctx,const uint8_t * src,size_t count)956 pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, const uint8_t *src,
957 size_t count)
958 {
959 assert(ctx);
960
961 if (!ctx->cursor || !src) {
962 return pldm_msgbuf_status(ctx, EINVAL);
963 }
964
965 if (!count) {
966 return 0;
967 }
968
969 #if INTMAX_MAX < SIZE_MAX
970 if (count > INTMAX_MAX) {
971 return pldm_msgbuf_status(ctx, EOVERFLOW);
972 }
973 #endif
974
975 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
976 return pldm_msgbuf_status(ctx, EOVERFLOW);
977 }
978 ctx->remaining -= (intmax_t)count;
979 assert(ctx->remaining >= 0);
980 if (ctx->remaining < 0) {
981 return pldm_msgbuf_status(ctx, EOVERFLOW);
982 }
983
984 memcpy(ctx->cursor, src, count);
985 ctx->cursor += count;
986
987 return 0;
988 }
989
990 #define pldm_msgbuf_insert_array(dst, src, count) \
991 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \
992 count)
993
994 __attribute__((always_inline)) static inline int
pldm_msgbuf_span_required(struct pldm_msgbuf * ctx,size_t required,void ** cursor)995 pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, size_t required,
996 void **cursor)
997 {
998 assert(ctx);
999
1000 if (!ctx->cursor || !cursor || *cursor) {
1001 return pldm_msgbuf_status(ctx, EINVAL);
1002 }
1003
1004 #if INTMAX_MAX < SIZE_MAX
1005 if (required > INTMAX_MAX) {
1006 return pldm_msgbuf_status(ctx, EOVERFLOW);
1007 }
1008 #endif
1009
1010 if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
1011 return pldm_msgbuf_status(ctx, EOVERFLOW);
1012 }
1013 ctx->remaining -= (intmax_t)required;
1014 assert(ctx->remaining >= 0);
1015 if (ctx->remaining < 0) {
1016 return pldm_msgbuf_status(ctx, EOVERFLOW);
1017 }
1018
1019 *cursor = ctx->cursor;
1020 ctx->cursor += required;
1021
1022 return 0;
1023 }
1024
1025 __attribute__((always_inline)) static inline int
pldm_msgbuf_span_remaining(struct pldm_msgbuf * ctx,void ** cursor,size_t * len)1026 pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
1027 {
1028 assert(ctx);
1029
1030 if (!ctx->cursor || !cursor || *cursor || !len) {
1031 return pldm_msgbuf_status(ctx, EINVAL);
1032 }
1033
1034 assert(ctx->remaining >= 0);
1035 if (ctx->remaining < 0) {
1036 return pldm_msgbuf_status(ctx, EOVERFLOW);
1037 }
1038
1039 *cursor = ctx->cursor;
1040 ctx->cursor += ctx->remaining;
1041 *len = ctx->remaining;
1042 ctx->remaining = 0;
1043
1044 return 0;
1045 }
1046
1047 /**
1048 * @brief pldm_msgbuf copy data between two msg buffers
1049 *
1050 * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1051 * @param[in,out] dst - destination of copy from source
1052 * @param[in] size - size of data to be copied
1053 * @param[in] description - description of data copied
1054 *
1055 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1056 * PLDM_ERROR_INVALID_LENGTH otherwise.
1057 * PLDM_ERROR_INVALID_DATA if input is invalid
1058 */
1059 #define pldm_msgbuf_copy(dst, src, type, name) \
1060 pldm__msgbuf_copy(dst, src, sizeof(type), #name)
1061 __attribute__((always_inline)) static inline int
1062 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_copy(struct pldm_msgbuf * dst,struct pldm_msgbuf * src,size_t size,const char * description)1063 pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1064 const char *description)
1065 {
1066 assert(src);
1067 assert(dst);
1068 assert(src->mode == dst->mode);
1069
1070 if (!src->cursor || !dst->cursor || !description) {
1071 return pldm_msgbuf_status(dst, EINVAL);
1072 }
1073
1074 #if INTMAX_MAX < SIZE_MAX
1075 if (size > INTMAX_MAX) {
1076 return pldm_msgbuf_status(dst, EOVERFLOW);
1077 }
1078 #endif
1079
1080 if (src->remaining < INTMAX_MIN + (intmax_t)size) {
1081 return pldm_msgbuf_status(dst, EOVERFLOW);
1082 }
1083
1084 if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
1085 return pldm_msgbuf_status(dst, EOVERFLOW);
1086 }
1087
1088 src->remaining -= (intmax_t)size;
1089 assert(src->remaining >= 0);
1090 if (src->remaining < 0) {
1091 return pldm_msgbuf_status(dst, EOVERFLOW);
1092 }
1093
1094 dst->remaining -= (intmax_t)size;
1095 assert(dst->remaining >= 0);
1096 if (dst->remaining < 0) {
1097 return pldm_msgbuf_status(dst, EOVERFLOW);
1098 }
1099
1100 memcpy(dst->cursor, src->cursor, size);
1101 src->cursor += size;
1102 dst->cursor += size;
1103
1104 return 0;
1105 }
1106
1107 #ifdef __cplusplus
1108 }
1109 #endif
1110
1111 #ifdef __cplusplus
1112 #include <type_traits>
1113
1114 template <typename T>
pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf * ctx,void * buf)1115 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
1116 void *buf)
1117 {
1118 static_assert(std::is_same<uint8_t *, T>::value);
1119 return pldm__msgbuf_extract_uint8(ctx, buf);
1120 }
1121
1122 template <typename T>
pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf * ctx,void * buf)1123 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
1124 void *buf)
1125 {
1126 static_assert(std::is_same<int8_t *, T>::value);
1127 return pldm__msgbuf_extract_int8(ctx, buf);
1128 }
1129
1130 template <typename T>
pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf * ctx,void * buf)1131 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
1132 void *buf)
1133 {
1134 static_assert(std::is_same<uint16_t *, T>::value);
1135 return pldm__msgbuf_extract_uint16(ctx, buf);
1136 }
1137
1138 template <typename T>
pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf * ctx,void * buf)1139 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
1140 void *buf)
1141 {
1142 static_assert(std::is_same<int16_t *, T>::value);
1143 return pldm__msgbuf_extract_int16(ctx, buf);
1144 }
1145
1146 template <typename T>
pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf * ctx,void * buf)1147 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
1148 void *buf)
1149 {
1150 static_assert(std::is_same<uint32_t *, T>::value);
1151 return pldm__msgbuf_extract_uint32(ctx, buf);
1152 }
1153
1154 template <typename T>
pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf * ctx,void * buf)1155 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
1156 void *buf)
1157 {
1158 static_assert(std::is_same<int32_t *, T>::value);
1159 return pldm__msgbuf_extract_int32(ctx, buf);
1160 }
1161
1162 template <typename T>
pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf * ctx,void * buf)1163 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
1164 void *buf)
1165 {
1166 static_assert(std::is_same<real32_t *, T>::value);
1167 return pldm__msgbuf_extract_real32(ctx, buf);
1168 }
1169 #endif
1170
1171 #endif /* BUF_H */
1172