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 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) 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 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 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 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 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 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 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 instantation, 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[inout] 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) 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) 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) 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) 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) 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) 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) 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 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 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 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 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 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 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 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 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 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 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[inout] src - pldm_msgbuf for source from where value should be copied 1051 * @param[inout] 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) 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> 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> 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> 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> 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> 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> 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> 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