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