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