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