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 LIBPLDM_CC_NONNULL 80 LIBPLDM_CC_ALWAYS_INLINE 81 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 82 void pldm__msgbuf_cleanup(struct pldm_msgbuf *ctx LIBPLDM_CC_UNUSED) 83 { 84 assert(ctx->cursor == NULL && ctx->remaining == INTMAX_MIN); 85 } 86 87 #ifdef __cplusplus 88 // NOLINTBEGIN(bugprone-macro-parentheses) 89 #define PLDM_MSGBUF_DEFINE_P(name) \ 90 struct pldm_msgbuf _##name LIBPLDM_CC_CLEANUP( \ 91 pldm__msgbuf_cleanup) = { NULL, INTMAX_MIN }; \ 92 auto *name = &(_##name) 93 // NOLINTEND(bugprone-macro-parentheses) 94 #else 95 #define PLDM_MSGBUF_DEFINE_P(name) \ 96 struct pldm_msgbuf _##name LIBPLDM_CC_CLEANUP( \ 97 pldm__msgbuf_cleanup) = { NULL, INTMAX_MIN }; \ 98 struct pldm_msgbuf *(name) = &(_##name) 99 #endif 100 101 LIBPLDM_CC_NONNULL 102 LIBPLDM_CC_ALWAYS_INLINE 103 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 104 int pldm__msgbuf_invalidate(struct pldm_msgbuf *ctx) 105 { 106 ctx->remaining = INTMAX_MIN; 107 return -EOVERFLOW; 108 } 109 110 /** 111 * @brief Initialize pldm buf struct for buf extractor 112 * 113 * @param[out] ctx - pldm_msgbuf context for extractor 114 * @param[in] minsize - The minimum required length of buffer `buf` 115 * @param[in] buf - buffer to be extracted 116 * @param[in] len - size of buffer 117 * 118 * @return 0 on success, otherwise an error code appropriate for the current 119 * personality. 120 */ 121 LIBPLDM_CC_NONNULL 122 LIBPLDM_CC_ALWAYS_INLINE 123 LIBPLDM_CC_WARN_UNUSED_RESULT 124 int 125 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 126 pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx, size_t minsize, const void *buf, 127 size_t len) 128 { 129 ctx->cursor = NULL; 130 131 if ((minsize > len)) { 132 return pldm__msgbuf_invalidate(ctx); 133 } 134 135 #if INTMAX_MAX < SIZE_MAX 136 if (len > INTMAX_MAX) { 137 return pldm__msgbuf_invalidate(ctx); 138 } 139 #endif 140 141 if (UINTPTR_MAX - (uintptr_t)buf < len) { 142 return pldm__msgbuf_invalidate(ctx); 143 } 144 145 ctx->cursor = (uint8_t *)buf; 146 ctx->remaining = (intmax_t)len; 147 148 return 0; 149 } 150 151 /** 152 * @brief Validate buffer overflow state 153 * 154 * @param[in] ctx - pldm_msgbuf context for extractor 155 * 156 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain 157 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a 158 * prior accesses would have occurred beyond the bounds of the buffer, and 159 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 160 * pointer. 161 */ 162 LIBPLDM_CC_NONNULL 163 LIBPLDM_CC_ALWAYS_INLINE 164 LIBPLDM_CC_WARN_UNUSED_RESULT 165 int pldm_msgbuf_validate(struct pldm_msgbuf *ctx) 166 { 167 if (ctx->remaining < 0) { 168 return -EOVERFLOW; 169 } 170 171 return 0; 172 } 173 174 /** 175 * @brief Test whether a message buffer has been exactly consumed 176 * 177 * @param[in] ctx - pldm_msgbuf context for extractor 178 * 179 * @return 0 iff there are zero bytes of data that remain unread from the buffer 180 * and no overflow has occurred. Otherwise, -EBADMSG if the buffer has not been 181 * completely consumed, or -EOVERFLOW if accesses were attempted beyond the 182 * bounds of the buffer. 183 */ 184 LIBPLDM_CC_NONNULL 185 LIBPLDM_CC_ALWAYS_INLINE 186 LIBPLDM_CC_WARN_UNUSED_RESULT 187 int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx) 188 { 189 if (ctx->remaining > 0) { 190 return -EBADMSG; 191 } 192 193 if (ctx->remaining < 0) { 194 return -EOVERFLOW; 195 } 196 197 return 0; 198 } 199 200 /** 201 * @brief End use of a msgbuf under error conditions 202 * 203 * @param[in] ctx - The msgbuf instance to discard 204 * @param[in] error - The error value to propagate 205 * 206 * Under normal conditions use of a msgbuf instance must be ended using @ref 207 * pldm_msgbuf_complete or one of its related APIs. Under error conditions, @ref 208 * pldm_msgbuf_discard should be used instead, as it makes it straight-forward 209 * to finalise the msgbuf while propagating the existing error code. 210 * 211 * @return The value provided in @param error 212 */ 213 LIBPLDM_CC_NONNULL 214 LIBPLDM_CC_ALWAYS_INLINE 215 LIBPLDM_CC_WARN_UNUSED_RESULT 216 int pldm_msgbuf_discard(struct pldm_msgbuf *ctx, int error) 217 { 218 ctx->cursor = NULL; 219 pldm__msgbuf_invalidate(ctx); 220 return error; 221 } 222 223 /** 224 * @brief Complete the pldm_msgbuf instance 225 * 226 * @param[in] ctx - pldm_msgbuf context for extractor 227 * 228 * @return 0 if all buffer accesses were in-bounds, -EOVERFLOW otherwise. 229 */ 230 LIBPLDM_CC_NONNULL 231 LIBPLDM_CC_ALWAYS_INLINE 232 LIBPLDM_CC_WARN_UNUSED_RESULT 233 int pldm_msgbuf_complete(struct pldm_msgbuf *ctx) 234 { 235 return pldm_msgbuf_discard(ctx, pldm_msgbuf_validate(ctx)); 236 } 237 238 /** 239 * @brief Complete the pldm_msgbuf instance, and check that the underlying buffer 240 * has been entirely consumed without overflow 241 * 242 * @param[in] ctx - pldm_msgbuf context 243 * 244 * @return 0 if all buffer access were in-bounds and completely consume the 245 * underlying buffer. Otherwise, -EBADMSG if the buffer has not been completely 246 * consumed, or -EOVERFLOW if accesses were attempted beyond the bounds of the 247 * buffer. 248 */ 249 LIBPLDM_CC_NONNULL 250 LIBPLDM_CC_ALWAYS_INLINE 251 LIBPLDM_CC_WARN_UNUSED_RESULT 252 int pldm_msgbuf_complete_consumed(struct pldm_msgbuf *ctx) 253 { 254 return pldm_msgbuf_discard(ctx, pldm_msgbuf_consumed(ctx)); 255 } 256 257 /* 258 * Exploit the pre-processor to perform type checking by macro substitution. 259 * 260 * A C type is defined by its alignment as well as its object 261 * size, and compilers have a hammer to enforce it in the form of 262 * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in 263 * the libpldm public API this presents a problem: Naively attempting to use the 264 * msgbuf APIs on a member of a packed struct would yield an error. 265 * 266 * The msgbuf APIs are implemented such that data is moved through unaligned 267 * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must 268 * make the object pointers take a trip through `void *` at its API boundary. 269 * That presents a bit too much of an opportunity to non-surgically remove your 270 * own foot, so here we set about doing something to mitigate that as well. 271 * 272 * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness 273 * only for the purpose of object sizes, disregarding alignment. We have a few 274 * constraints that cause some headaches: 275 * 276 * 1. We have to perform the type-check before a call through a C function, 277 * as the function must take the object pointer argument as `void *`. 278 * Essentially, this constrains us to doing something with macros. 279 * 280 * 2. While libpldm is a C library, its test suite is written in C++ to take 281 * advantage of gtest. 282 * 283 * 3. Ideally we'd do something with C's `static_assert()`, however 284 * `static_assert()` is defined as void, and as we're constrained to macros, 285 * using `static_assert()` would require a statement-expression 286 * 287 * 4. Currently the project is built with `-std=c17`. CPP statement-expressions 288 * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for 289 * the purpose of enabling statement-expressions in this one instance. 290 * 291 * 5. We can achieve a conditional build error using `pldm_require_obj_type()`, 292 * however it's implemented in terms of `_Generic()`, which is not available 293 * in C++. 294 * 295 * Combined this means we need separate solutions for C and C++. 296 * 297 * For C, as we don't have statement-expressions, we need to exploit some other 298 * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf 299 * API function call. We also have to take care of the fact that the call-sites 300 * may be in the context of a variable assignment for error-handling purposes. 301 * The key observation is that we can use the comma operator as a sequence point 302 * to order the type check before the API call, discarding the "result" value of 303 * the type check and yielding the return value of the API call. 304 * 305 * C++ could be less of a headache than the C as we can leverage template 306 * functions. An advantage of template functions is that while their definition 307 * is driven by instantion, the definition does not appear at the source 308 * location of the instantiation, which gives it a great leg-up over the problems 309 * we have in the C path. However, the use of the msgbuf APIs in the test suite 310 * still makes things somewhat tricky, as the call-sites in the test suite are 311 * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that 312 * takes both the object type and the required type as template arguments, and 313 * then define the object pointer parameter as `void *` for a call through to 314 * the appropriate msgbuf API. However, because the msgbuf API call-sites are 315 * encapsulated in gtest macros, use of commas in the template specification 316 * causes pre-processor confusion. In this way we're constrained to only one 317 * template argument per function. 318 * 319 * Implement the C++ path using template functions that take the destination 320 * object type as a template argument, while the name of the function symbols 321 * are derived from the required type. The manual implementations of these 322 * appear at the end of the header. The type safety is actually enforced 323 * by `static_assert()` this time, as we can use statements as we're not 324 * constrained to an expression in the templated function body. 325 * 326 * The invocations of pldm_msgbuf_extract_typecheck() typically result in 327 * double-evaluation of some arguments. We're not yet bothered by this for two 328 * reasons: 329 * 330 * 1. The nature of the current call-sites are such that there are no 331 * argument expressions that result in undesirable side-effects 332 * 333 * 2. It's an API internal to the libpldm implementation, and we can fix things 334 * whenever something crops up the violates the observation in 1. 335 */ 336 #ifdef __cplusplus 337 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \ 338 pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__) 339 #else 340 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \ 341 (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__)) 342 #endif 343 344 /** 345 * @brief pldm_msgbuf extractor for a uint8_t 346 * 347 * @param[in,out] ctx - pldm_msgbuf context for extractor 348 * @param[out] dst - destination of extracted value 349 * 350 * @return PLDM_SUCCESS if buffer accesses were in-bounds, 351 * PLDM_ERROR_INVALID_LENGTH otherwise. 352 * PLDM_ERROR_INVALID_DATA if input a invalid ctx 353 */ 354 #define pldm_msgbuf_extract_uint8(ctx, dst) \ 355 pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \ 356 dst, ctx, (void *)&(dst)) 357 LIBPLDM_CC_NONNULL 358 LIBPLDM_CC_ALWAYS_INLINE int 359 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 360 pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst) 361 { 362 if (ctx->remaining >= (intmax_t)sizeof(uint8_t)) { 363 assert(ctx->cursor); 364 memcpy(dst, ctx->cursor, sizeof(uint8_t)); 365 ctx->cursor++; 366 ctx->remaining -= sizeof(uint8_t); 367 return 0; 368 } 369 370 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(uint8_t)) { 371 ctx->remaining -= sizeof(uint8_t); 372 return -EOVERFLOW; 373 } 374 375 return pldm__msgbuf_invalidate(ctx); 376 } 377 378 #define pldm_msgbuf_extract_int8(ctx, dst) \ 379 pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \ 380 ctx, (void *)&(dst)) 381 LIBPLDM_CC_NONNULL 382 LIBPLDM_CC_ALWAYS_INLINE int 383 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 384 pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst) 385 { 386 if (ctx->remaining >= (intmax_t)sizeof(int8_t)) { 387 assert(ctx->cursor); 388 memcpy(dst, ctx->cursor, sizeof(int8_t)); 389 ctx->cursor++; 390 ctx->remaining -= sizeof(int8_t); 391 return 0; 392 } 393 394 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(int8_t)) { 395 ctx->remaining -= sizeof(int8_t); 396 return -EOVERFLOW; 397 } 398 399 return pldm__msgbuf_invalidate(ctx); 400 } 401 402 #define pldm_msgbuf_extract_uint16(ctx, dst) \ 403 pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \ 404 dst, ctx, (void *)&(dst)) 405 LIBPLDM_CC_NONNULL 406 LIBPLDM_CC_ALWAYS_INLINE int 407 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 408 pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst) 409 { 410 uint16_t ldst; 411 412 // Check for underflow while tracking the magnitude of the buffer overflow 413 static_assert( 414 // NOLINTNEXTLINE(bugprone-sizeof-expression) 415 sizeof(ldst) < INTMAX_MAX, 416 "The following addition may not uphold the runtime assertion"); 417 418 if (ctx->remaining >= (intmax_t)sizeof(ldst)) { 419 assert(ctx->cursor); 420 421 // Use memcpy() to have the compiler deal with any alignment 422 // issues on the target architecture 423 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 424 425 // Only assign the target value once it's correctly decoded 426 ldst = le16toh(ldst); 427 428 // Allow storing to unaligned 429 memcpy(dst, &ldst, sizeof(ldst)); 430 431 ctx->cursor += sizeof(ldst); 432 ctx->remaining -= sizeof(ldst); 433 return 0; 434 } 435 436 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { 437 ctx->remaining -= sizeof(ldst); 438 return -EOVERFLOW; 439 } 440 441 return pldm__msgbuf_invalidate(ctx); 442 } 443 444 #define pldm_msgbuf_extract_int16(ctx, dst) \ 445 pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \ 446 dst, ctx, (void *)&(dst)) 447 LIBPLDM_CC_NONNULL 448 LIBPLDM_CC_ALWAYS_INLINE int 449 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 450 pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst) 451 { 452 int16_t ldst; 453 454 static_assert( 455 // NOLINTNEXTLINE(bugprone-sizeof-expression) 456 sizeof(ldst) < INTMAX_MAX, 457 "The following addition may not uphold the runtime assertion"); 458 459 if (ctx->remaining >= (intmax_t)sizeof(ldst)) { 460 assert(ctx->cursor); 461 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 462 ldst = le16toh(ldst); 463 memcpy(dst, &ldst, sizeof(ldst)); 464 ctx->cursor += sizeof(ldst); 465 ctx->remaining -= sizeof(ldst); 466 return 0; 467 } 468 469 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { 470 ctx->remaining -= sizeof(ldst); 471 return -EOVERFLOW; 472 } 473 474 return pldm__msgbuf_invalidate(ctx); 475 } 476 477 #define pldm_msgbuf_extract_uint32(ctx, dst) \ 478 pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \ 479 dst, ctx, (void *)&(dst)) 480 LIBPLDM_CC_NONNULL 481 LIBPLDM_CC_ALWAYS_INLINE int 482 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 483 pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst) 484 { 485 uint32_t ldst; 486 487 static_assert( 488 // NOLINTNEXTLINE(bugprone-sizeof-expression) 489 sizeof(ldst) < INTMAX_MAX, 490 "The following addition may not uphold the runtime assertion"); 491 492 if (ctx->remaining >= (intmax_t)sizeof(ldst)) { 493 assert(ctx->cursor); 494 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 495 ldst = le32toh(ldst); 496 memcpy(dst, &ldst, sizeof(ldst)); 497 ctx->cursor += sizeof(ldst); 498 ctx->remaining -= sizeof(ldst); 499 return 0; 500 } 501 502 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { 503 ctx->remaining -= sizeof(ldst); 504 return -EOVERFLOW; 505 } 506 507 return pldm__msgbuf_invalidate(ctx); 508 } 509 510 #define pldm_msgbuf_extract_int32(ctx, dst) \ 511 pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \ 512 dst, ctx, (void *)&(dst)) 513 LIBPLDM_CC_NONNULL 514 LIBPLDM_CC_ALWAYS_INLINE int 515 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 516 pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst) 517 { 518 int32_t ldst; 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 assert(ctx->cursor); 527 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 528 ldst = le32toh(ldst); 529 memcpy(dst, &ldst, sizeof(ldst)); 530 ctx->cursor += sizeof(ldst); 531 ctx->remaining -= sizeof(ldst); 532 return 0; 533 } 534 535 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { 536 ctx->remaining -= sizeof(ldst); 537 return -EOVERFLOW; 538 } 539 540 return pldm__msgbuf_invalidate(ctx); 541 } 542 543 #define pldm_msgbuf_extract_real32(ctx, dst) \ 544 pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \ 545 dst, ctx, (void *)&(dst)) 546 LIBPLDM_CC_NONNULL 547 LIBPLDM_CC_ALWAYS_INLINE int 548 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 549 pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst) 550 { 551 uint32_t ldst; 552 553 static_assert(sizeof(real32_t) == sizeof(ldst), 554 "Mismatched type sizes for dst and ldst"); 555 556 static_assert( 557 // NOLINTNEXTLINE(bugprone-sizeof-expression) 558 sizeof(ldst) < INTMAX_MAX, 559 "The following addition may not uphold the runtime assertion"); 560 561 if (ctx->remaining >= (intmax_t)sizeof(ldst)) { 562 assert(ctx->cursor); 563 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 564 ldst = le32toh(ldst); 565 memcpy(dst, &ldst, sizeof(ldst)); 566 ctx->cursor += sizeof(ldst); 567 ctx->remaining -= sizeof(ldst); 568 return 0; 569 } 570 571 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { 572 ctx->remaining -= sizeof(ldst); 573 return -EOVERFLOW; 574 } 575 576 return pldm__msgbuf_invalidate(ctx); 577 } 578 579 /** 580 * Extract the field at the msgbuf cursor into the lvalue named by dst. 581 * 582 * @param ctx The msgbuf context object 583 * @param dst The lvalue into which the field at the msgbuf cursor should be 584 * extracted 585 * 586 * @return PLDM_SUCCESS on success, otherwise another value on error 587 */ 588 #define pldm_msgbuf_extract(ctx, dst) \ 589 _Generic((dst), \ 590 uint8_t: pldm__msgbuf_extract_uint8, \ 591 int8_t: pldm__msgbuf_extract_int8, \ 592 uint16_t: pldm__msgbuf_extract_uint16, \ 593 int16_t: pldm__msgbuf_extract_int16, \ 594 uint32_t: pldm__msgbuf_extract_uint32, \ 595 int32_t: pldm__msgbuf_extract_int32, \ 596 real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst)) 597 598 /** 599 * Extract the field at the msgbuf cursor into the object pointed-to by dst. 600 * 601 * @param ctx The msgbuf context object 602 * @param dst The pointer to the object into which the field at the msgbuf 603 * cursor should be extracted 604 * 605 * @return PLDM_SUCCESS on success, otherwise another value on error 606 */ 607 #define pldm_msgbuf_extract_p(ctx, dst) \ 608 _Generic((dst), \ 609 uint8_t *: pldm__msgbuf_extract_uint8, \ 610 int8_t *: pldm__msgbuf_extract_int8, \ 611 uint16_t *: pldm__msgbuf_extract_uint16, \ 612 int16_t *: pldm__msgbuf_extract_int16, \ 613 uint32_t *: pldm__msgbuf_extract_uint32, \ 614 int32_t *: pldm__msgbuf_extract_int32, \ 615 real32_t *: pldm__msgbuf_extract_real32)(ctx, dst) 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 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 624 pldm__msgbuf_extract_array_void(struct pldm_msgbuf *ctx, size_t count, 625 void *dst, size_t dst_count) 626 { 627 if (count > dst_count) { 628 return -EINVAL; 629 } 630 631 if (!count) { 632 return 0; 633 } 634 635 #if INTMAX_MAX < SIZE_MAX 636 if (count > INTMAX_MAX) { 637 return pldm__msgbuf_invalidate(ctx); 638 } 639 #endif 640 641 if (ctx->remaining >= (intmax_t)count) { 642 assert(ctx->cursor); 643 memcpy(dst, ctx->cursor, count); 644 ctx->cursor += count; 645 ctx->remaining -= (intmax_t)count; 646 return 0; 647 } 648 649 if (ctx->remaining > INTMAX_MIN + (intmax_t)count) { 650 ctx->remaining -= (intmax_t)count; 651 return -EOVERFLOW; 652 } 653 654 return pldm__msgbuf_invalidate(ctx); 655 } 656 657 /** 658 * @ref pldm_msgbuf_extract_array 659 */ 660 LIBPLDM_CC_NONNULL 661 LIBPLDM_CC_WARN_UNUSED_RESULT 662 LIBPLDM_CC_ALWAYS_INLINE int 663 pldm_msgbuf_extract_array_char(struct pldm_msgbuf *ctx, size_t count, char *dst, 664 size_t dst_count) 665 { 666 return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count); 667 } 668 669 /** 670 * @ref pldm_msgbuf_extract_array 671 */ 672 LIBPLDM_CC_NONNULL 673 LIBPLDM_CC_WARN_UNUSED_RESULT 674 LIBPLDM_CC_ALWAYS_INLINE int 675 pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, size_t count, 676 uint8_t *dst, size_t dst_count) 677 { 678 return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count); 679 } 680 681 /** 682 * Extract an array of data from the msgbuf instance 683 * 684 * @param ctx - The msgbuf instance from which to extract an array of data 685 * @param count - The number of array elements to extract 686 * @param dst - The array object into which elements from @p ctx should be 687 extracted 688 * @param dst_count - The maximum number of elements to place into @p dst 689 * 690 * Note that both @p count and @p dst_count can only be counted by `sizeof` for 691 * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number 692 * of array elements and _not_ the object size of the array. 693 */ 694 #define pldm_msgbuf_extract_array(ctx, count, dst, dst_count) \ 695 _Generic((*(dst)), \ 696 uint8_t: pldm_msgbuf_extract_array_uint8, \ 697 char: pldm_msgbuf_extract_array_char)(ctx, count, dst, \ 698 dst_count) 699 700 LIBPLDM_CC_NONNULL 701 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint64(struct pldm_msgbuf *ctx, 702 const uint64_t src) 703 { 704 uint64_t val = htole64(src); 705 706 static_assert( 707 // NOLINTNEXTLINE(bugprone-sizeof-expression) 708 sizeof(src) < INTMAX_MAX, 709 "The following addition may not uphold the runtime assertion"); 710 711 if (ctx->remaining >= (intmax_t)sizeof(src)) { 712 assert(ctx->cursor); 713 memcpy(ctx->cursor, &val, sizeof(val)); 714 ctx->cursor += sizeof(src); 715 ctx->remaining -= sizeof(src); 716 return 0; 717 } 718 719 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { 720 ctx->remaining -= sizeof(src); 721 return -EOVERFLOW; 722 } 723 724 return pldm__msgbuf_invalidate(ctx); 725 } 726 727 LIBPLDM_CC_NONNULL 728 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, 729 const uint32_t src) 730 { 731 uint32_t val = htole32(src); 732 733 static_assert( 734 // NOLINTNEXTLINE(bugprone-sizeof-expression) 735 sizeof(src) < INTMAX_MAX, 736 "The following addition may not uphold the runtime assertion"); 737 738 if (ctx->remaining >= (intmax_t)sizeof(src)) { 739 assert(ctx->cursor); 740 memcpy(ctx->cursor, &val, sizeof(val)); 741 ctx->cursor += sizeof(src); 742 ctx->remaining -= sizeof(src); 743 return 0; 744 } 745 746 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { 747 ctx->remaining -= sizeof(src); 748 return -EOVERFLOW; 749 } 750 751 return pldm__msgbuf_invalidate(ctx); 752 } 753 754 LIBPLDM_CC_NONNULL 755 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, 756 const uint16_t src) 757 { 758 uint16_t val = htole16(src); 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 765 if (ctx->remaining >= (intmax_t)sizeof(src)) { 766 assert(ctx->cursor); 767 memcpy(ctx->cursor, &val, sizeof(val)); 768 ctx->cursor += sizeof(src); 769 ctx->remaining -= sizeof(src); 770 return 0; 771 } 772 773 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { 774 ctx->remaining -= sizeof(src); 775 return -EOVERFLOW; 776 } 777 778 return pldm__msgbuf_invalidate(ctx); 779 } 780 781 LIBPLDM_CC_NONNULL 782 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, 783 const uint8_t src) 784 { 785 static_assert( 786 // NOLINTNEXTLINE(bugprone-sizeof-expression) 787 sizeof(src) < INTMAX_MAX, 788 "The following addition may not uphold the runtime assertion"); 789 790 if (ctx->remaining >= (intmax_t)sizeof(src)) { 791 assert(ctx->cursor); 792 memcpy(ctx->cursor, &src, sizeof(src)); 793 ctx->cursor += sizeof(src); 794 ctx->remaining -= sizeof(src); 795 return 0; 796 } 797 798 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { 799 ctx->remaining -= sizeof(src); 800 return -EOVERFLOW; 801 } 802 803 return pldm__msgbuf_invalidate(ctx); 804 } 805 806 LIBPLDM_CC_NONNULL 807 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, 808 const int32_t src) 809 { 810 int32_t val = htole32(src); 811 812 static_assert( 813 // NOLINTNEXTLINE(bugprone-sizeof-expression) 814 sizeof(src) < INTMAX_MAX, 815 "The following addition may not uphold the runtime assertion"); 816 817 if (ctx->remaining >= (intmax_t)sizeof(src)) { 818 assert(ctx->cursor); 819 memcpy(ctx->cursor, &val, sizeof(val)); 820 ctx->cursor += sizeof(src); 821 ctx->remaining -= sizeof(src); 822 return 0; 823 } 824 825 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { 826 ctx->remaining -= sizeof(src); 827 return -EOVERFLOW; 828 } 829 830 return pldm__msgbuf_invalidate(ctx); 831 } 832 833 LIBPLDM_CC_NONNULL 834 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, 835 const int16_t src) 836 { 837 int16_t val = htole16(src); 838 839 static_assert( 840 // NOLINTNEXTLINE(bugprone-sizeof-expression) 841 sizeof(src) < INTMAX_MAX, 842 "The following addition may not uphold the runtime assertion"); 843 844 if (ctx->remaining >= (intmax_t)sizeof(src)) { 845 assert(ctx->cursor); 846 memcpy(ctx->cursor, &val, sizeof(val)); 847 ctx->cursor += sizeof(src); 848 ctx->remaining -= sizeof(src); 849 return 0; 850 } 851 852 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { 853 ctx->remaining -= sizeof(src); 854 return -EOVERFLOW; 855 } 856 857 return pldm__msgbuf_invalidate(ctx); 858 } 859 860 LIBPLDM_CC_NONNULL 861 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, 862 const int8_t src) 863 { 864 static_assert( 865 // NOLINTNEXTLINE(bugprone-sizeof-expression) 866 sizeof(src) < INTMAX_MAX, 867 "The following addition may not uphold the runtime assertion"); 868 869 if (ctx->remaining >= (intmax_t)sizeof(src)) { 870 assert(ctx->cursor); 871 memcpy(ctx->cursor, &src, sizeof(src)); 872 ctx->cursor += sizeof(src); 873 ctx->remaining -= sizeof(src); 874 return 0; 875 } 876 877 if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { 878 ctx->remaining -= sizeof(src); 879 return -EOVERFLOW; 880 } 881 882 return pldm__msgbuf_invalidate(ctx); 883 } 884 885 #define pldm_msgbuf_insert(dst, src) \ 886 _Generic((src), \ 887 uint8_t: pldm_msgbuf_insert_uint8, \ 888 int8_t: pldm_msgbuf_insert_int8, \ 889 uint16_t: pldm_msgbuf_insert_uint16, \ 890 int16_t: pldm_msgbuf_insert_int16, \ 891 uint32_t: pldm_msgbuf_insert_uint32, \ 892 int32_t: pldm_msgbuf_insert_int32, \ 893 uint64_t: pldm_msgbuf_insert_uint64)(dst, src) 894 895 /** 896 * @ref pldm_msgbuf_insert_array 897 */ 898 LIBPLDM_CC_NONNULL 899 LIBPLDM_CC_WARN_UNUSED_RESULT 900 LIBPLDM_CC_ALWAYS_INLINE int 901 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 902 pldm__msgbuf_insert_array_void(struct pldm_msgbuf *ctx, size_t count, 903 const void *src, size_t src_count) 904 { 905 if (count > src_count) { 906 return -EINVAL; 907 } 908 909 if (!count) { 910 return 0; 911 } 912 913 #if INTMAX_MAX < SIZE_MAX 914 if (count > INTMAX_MAX) { 915 return pldm__msgbuf_invalidate(ctx); 916 } 917 #endif 918 919 if (ctx->remaining >= (intmax_t)count) { 920 assert(ctx->cursor); 921 memcpy(ctx->cursor, src, count); 922 ctx->cursor += count; 923 ctx->remaining -= (intmax_t)count; 924 return 0; 925 } 926 927 if (ctx->remaining > INTMAX_MIN + (intmax_t)count) { 928 ctx->remaining -= (intmax_t)count; 929 return -EOVERFLOW; 930 } 931 932 return pldm__msgbuf_invalidate(ctx); 933 } 934 935 /** 936 * @ref pldm_msgbuf_insert_array 937 */ 938 LIBPLDM_CC_NONNULL 939 LIBPLDM_CC_WARN_UNUSED_RESULT 940 LIBPLDM_CC_ALWAYS_INLINE int 941 pldm_msgbuf_insert_array_char(struct pldm_msgbuf *ctx, size_t count, 942 const char *src, size_t src_count) 943 { 944 return pldm__msgbuf_insert_array_void(ctx, count, src, src_count); 945 } 946 947 /** 948 * @ref pldm_msgbuf_insert_array 949 */ 950 LIBPLDM_CC_NONNULL 951 LIBPLDM_CC_WARN_UNUSED_RESULT 952 LIBPLDM_CC_ALWAYS_INLINE int 953 pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, size_t count, 954 const uint8_t *src, size_t src_count) 955 { 956 return pldm__msgbuf_insert_array_void(ctx, count, src, src_count); 957 } 958 959 /** 960 * Insert an array of data into the msgbuf instance 961 * 962 * @param ctx - The msgbuf instance into which the array of data should be 963 * inserted 964 * @param count - The number of array elements to insert 965 * @param src - The array object from which elements should be inserted into 966 @p ctx 967 * @param src_count - The maximum number of elements to insert from @p src 968 * 969 * Note that both @p count and @p src_count can only be counted by `sizeof` for 970 * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number 971 * of array elements and _not_ the object size of the array. 972 */ 973 #define pldm_msgbuf_insert_array(dst, count, src, src_count) \ 974 _Generic((*(src)), \ 975 uint8_t: pldm_msgbuf_insert_array_uint8, \ 976 char: pldm_msgbuf_insert_array_char)(dst, count, src, \ 977 src_count) 978 979 LIBPLDM_CC_NONNULL_ARGS(1) 980 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, 981 size_t required, 982 void **cursor) 983 { 984 #if INTMAX_MAX < SIZE_MAX 985 if (required > INTMAX_MAX) { 986 return pldm__msgbuf_invalidate(ctx); 987 } 988 #endif 989 990 if (ctx->remaining >= (intmax_t)required) { 991 assert(ctx->cursor); 992 if (cursor) { 993 *cursor = ctx->cursor; 994 } 995 ctx->cursor += required; 996 ctx->remaining -= (intmax_t)required; 997 return 0; 998 } 999 1000 if (ctx->remaining > INTMAX_MIN + (intmax_t)required) { 1001 ctx->remaining -= (intmax_t)required; 1002 return -EOVERFLOW; 1003 } 1004 1005 return pldm__msgbuf_invalidate(ctx); 1006 } 1007 1008 LIBPLDM_CC_NONNULL_ARGS(1) 1009 LIBPLDM_CC_ALWAYS_INLINE int 1010 pldm_msgbuf_span_string_ascii(struct pldm_msgbuf *ctx, void **cursor, 1011 size_t *length) 1012 { 1013 intmax_t measured; 1014 1015 if (ctx->remaining < 0) { 1016 return pldm__msgbuf_invalidate(ctx); 1017 } 1018 assert(ctx->cursor); 1019 1020 measured = (intmax_t)strnlen((const char *)ctx->cursor, ctx->remaining); 1021 if (measured == ctx->remaining) { 1022 return pldm__msgbuf_invalidate(ctx); 1023 } 1024 1025 /* Include the NUL terminator in the span length, as spans are opaque */ 1026 measured++; 1027 1028 if (ctx->remaining >= measured) { 1029 assert(ctx->cursor); 1030 if (cursor) { 1031 *cursor = ctx->cursor; 1032 } 1033 1034 ctx->cursor += measured; 1035 1036 if (length) { 1037 *length = measured; 1038 } 1039 1040 ctx->remaining -= measured; 1041 return 0; 1042 } 1043 1044 if (ctx->remaining > INTMAX_MIN + measured) { 1045 ctx->remaining -= measured; 1046 return -EOVERFLOW; 1047 } 1048 1049 return pldm__msgbuf_invalidate(ctx); 1050 } 1051 1052 LIBPLDM_CC_NONNULL_ARGS(1) 1053 LIBPLDM_CC_ALWAYS_INLINE int 1054 pldm_msgbuf_span_string_utf16(struct pldm_msgbuf *ctx, void **cursor, 1055 size_t *length) 1056 { 1057 static const char16_t term = 0; 1058 ptrdiff_t measured; 1059 void *end; 1060 1061 if (ctx->remaining < 0) { 1062 return pldm__msgbuf_invalidate(ctx); 1063 } 1064 assert(ctx->cursor); 1065 1066 /* 1067 * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do 1068 * not form a UTF16 NUL _code-point_ due to alignment with respect to the 1069 * start of the string 1070 */ 1071 end = ctx->cursor; 1072 do { 1073 if (end != ctx->cursor) { 1074 /* 1075 * If we've looped we've found a relatively-unaligned NUL code-point. 1076 * Scan again from a relatively-aligned start point. 1077 */ 1078 end = (char *)end + 1; 1079 } 1080 measured = (char *)end - (char *)ctx->cursor; 1081 end = memmem(end, ctx->remaining - measured, &term, 1082 sizeof(term)); 1083 } while (end && ((uintptr_t)end & 1) != ((uintptr_t)ctx->cursor & 1)); 1084 1085 if (!end) { 1086 /* 1087 * Optimistically, the last required pattern byte was one beyond the end of 1088 * the buffer. Setting ctx->remaining negative ensures the 1089 * `pldm_msgbuf_complete*()` APIs also return an error. 1090 */ 1091 return pldm__msgbuf_invalidate(ctx); 1092 } 1093 1094 end = (char *)end + sizeof(char16_t); 1095 measured = (char *)end - (char *)ctx->cursor; 1096 1097 #if INTMAX_MAX < PTRDIFF_MAX 1098 if (measured >= INTMAX_MAX) { 1099 return pldm__msgbuf_invalidate(ctx); 1100 } 1101 #endif 1102 1103 if (ctx->remaining >= (intmax_t)measured) { 1104 assert(ctx->cursor); 1105 if (cursor) { 1106 *cursor = ctx->cursor; 1107 } 1108 1109 ctx->cursor += measured; 1110 1111 if (length) { 1112 *length = (size_t)measured; 1113 } 1114 1115 ctx->remaining -= (intmax_t)measured; 1116 return 0; 1117 } 1118 1119 if (ctx->remaining > INTMAX_MIN + (intmax_t)measured) { 1120 ctx->remaining -= (intmax_t)measured; 1121 return -EOVERFLOW; 1122 } 1123 1124 return pldm__msgbuf_invalidate(ctx); 1125 } 1126 1127 LIBPLDM_CC_NONNULL 1128 LIBPLDM_CC_ALWAYS_INLINE int 1129 pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len) 1130 { 1131 if (ctx->remaining < 0) { 1132 return -EOVERFLOW; 1133 } 1134 1135 assert(ctx->cursor); 1136 *cursor = ctx->cursor; 1137 ctx->cursor += ctx->remaining; 1138 *len = ctx->remaining; 1139 ctx->remaining = 0; 1140 1141 return 0; 1142 } 1143 1144 LIBPLDM_CC_NONNULL_ARGS(1) 1145 LIBPLDM_CC_ALWAYS_INLINE 1146 int pldm_msgbuf_span_until(struct pldm_msgbuf *ctx, size_t trailer, 1147 void **cursor, size_t *length) 1148 { 1149 #if INTMAX_MAX < SIZE_MAX 1150 if (trailer > INTMAX_MAX) { 1151 return pldm__msgbuf_invalidate(ctx); 1152 } 1153 #endif 1154 1155 if (ctx->remaining >= (intmax_t)trailer) { 1156 ptrdiff_t delta; 1157 1158 assert(ctx->cursor); 1159 1160 delta = ctx->remaining - (intmax_t)trailer; 1161 if (cursor) { 1162 *cursor = ctx->cursor; 1163 } 1164 ctx->cursor += delta; 1165 if (length) { 1166 *length = delta; 1167 } 1168 ctx->remaining = (intmax_t)trailer; 1169 return 0; 1170 } 1171 1172 if (ctx->remaining > INTMAX_MIN + (intmax_t)trailer) { 1173 ctx->remaining = INTMAX_MIN + (intmax_t)trailer; 1174 return -EOVERFLOW; 1175 } 1176 1177 return pldm__msgbuf_invalidate(ctx); 1178 } 1179 1180 LIBPLDM_CC_NONNULL 1181 LIBPLDM_CC_ALWAYS_INLINE int 1182 pldm_msgbuf_peek_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len) 1183 { 1184 if (ctx->remaining < 0) { 1185 return -EOVERFLOW; 1186 } 1187 1188 assert(ctx->cursor); 1189 *cursor = ctx->cursor; 1190 *len = ctx->remaining; 1191 1192 return 0; 1193 } 1194 1195 LIBPLDM_CC_NONNULL 1196 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_skip(struct pldm_msgbuf *ctx, 1197 size_t count) 1198 { 1199 #if INTMAX_MAX < SIZE_MAX 1200 if (count > INTMAX_MAX) { 1201 return pldm__msgbuf_invalidate(ctx); 1202 } 1203 #endif 1204 1205 if (ctx->remaining >= (intmax_t)count) { 1206 assert(ctx->cursor); 1207 ctx->cursor += count; 1208 ctx->remaining -= (intmax_t)count; 1209 return 0; 1210 } 1211 1212 if (ctx->remaining > INTMAX_MIN + (intmax_t)count) { 1213 ctx->remaining -= (intmax_t)count; 1214 return -EOVERFLOW; 1215 } 1216 1217 return pldm__msgbuf_invalidate(ctx); 1218 } 1219 1220 /** 1221 * @brief Complete the pldm_msgbuf instance and return the number of bytes 1222 * consumed. 1223 * 1224 * @param ctx - The msgbuf. 1225 * @param orig_len - The original size of the msgbuf, the `len` argument passed to 1226 * pldm_msgbuf_init_errno(). 1227 * @param ret_used_len - The number of bytes that have been used from the msgbuf instance. 1228 * 1229 * This can be called after a number of pldm_msgbuf_insert...() calls to 1230 * determine the total size that was written. 1231 * 1232 * @return 0 on success, -EOVERFLOW if an implausible orig_len was provided or 1233 * an out-of-bounds access occurred. 1234 */ 1235 LIBPLDM_CC_NONNULL 1236 LIBPLDM_CC_ALWAYS_INLINE 1237 LIBPLDM_CC_WARN_UNUSED_RESULT 1238 int pldm_msgbuf_complete_used(struct pldm_msgbuf *ctx, size_t orig_len, 1239 size_t *ret_used_len) 1240 { 1241 int rc; 1242 1243 ctx->cursor = NULL; 1244 rc = pldm_msgbuf_validate(ctx); 1245 if (rc) { 1246 pldm__msgbuf_invalidate(ctx); 1247 return rc; 1248 } 1249 1250 if ((size_t)ctx->remaining > orig_len) { 1251 /* Caller passed incorrect orig_len */ 1252 return pldm__msgbuf_invalidate(ctx); 1253 } 1254 1255 *ret_used_len = orig_len - ctx->remaining; 1256 pldm__msgbuf_invalidate(ctx); 1257 return 0; 1258 } 1259 1260 /** 1261 * @brief pldm_msgbuf copy data between two msg buffers 1262 * 1263 * @param[in,out] src - pldm_msgbuf for source from where value should be copied 1264 * @param[in,out] dst - destination of copy from source 1265 * @param[in] size - size of data to be copied 1266 * @param[in] description - description of data copied 1267 * 1268 * @return PLDM_SUCCESS if buffer accesses were in-bounds, 1269 * PLDM_ERROR_INVALID_LENGTH otherwise. 1270 * PLDM_ERROR_INVALID_DATA if input is invalid 1271 */ 1272 #define pldm_msgbuf_copy(dst, src, type, name) \ 1273 pldm__msgbuf_copy(dst, src, sizeof(type), #name) 1274 LIBPLDM_CC_NONNULL 1275 LIBPLDM_CC_ALWAYS_INLINE int 1276 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 1277 pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size, 1278 const char *description LIBPLDM_CC_UNUSED) 1279 { 1280 #if INTMAX_MAX < SIZE_MAX 1281 if (size > INTMAX_MAX) { 1282 pldm__msgbuf_invalidate(src); 1283 pldm__msgbuf_invalidate(dst); 1284 return -EOVERFLOW; 1285 } 1286 #endif 1287 1288 if (src->remaining >= (intmax_t)size && 1289 dst->remaining >= (intmax_t)size) { 1290 assert(src->cursor && dst->cursor); 1291 memcpy(dst->cursor, src->cursor, size); 1292 src->cursor += size; 1293 src->remaining -= (intmax_t)size; 1294 dst->cursor += size; 1295 dst->remaining -= (intmax_t)size; 1296 return 0; 1297 } 1298 1299 if (src->remaining > INTMAX_MIN + (intmax_t)size) { 1300 src->remaining -= (intmax_t)size; 1301 } else { 1302 pldm__msgbuf_invalidate(src); 1303 } 1304 1305 if (dst->remaining > INTMAX_MIN + (intmax_t)size) { 1306 dst->remaining -= (intmax_t)size; 1307 } else { 1308 pldm__msgbuf_invalidate(dst); 1309 } 1310 1311 return -EOVERFLOW; 1312 } 1313 1314 LIBPLDM_CC_NONNULL 1315 LIBPLDM_CC_WARN_UNUSED_RESULT 1316 LIBPLDM_CC_ALWAYS_INLINE int 1317 pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src) 1318 { 1319 void *ascii = NULL; 1320 size_t len = 0; 1321 int rc; 1322 1323 rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len); 1324 if (rc < 0) { 1325 return rc; 1326 } 1327 1328 return pldm__msgbuf_insert_array_void(dst, len, ascii, len); 1329 } 1330 1331 LIBPLDM_CC_NONNULL 1332 LIBPLDM_CC_WARN_UNUSED_RESULT 1333 LIBPLDM_CC_ALWAYS_INLINE int 1334 pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src) 1335 { 1336 void *utf16 = NULL; 1337 size_t len = 0; 1338 int rc; 1339 1340 rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len); 1341 if (rc < 0) { 1342 return rc; 1343 } 1344 1345 return pldm__msgbuf_insert_array_void(dst, len, utf16, len); 1346 } 1347 1348 /** 1349 * @brief pldm_msgbuf uint8_t extractor for a size_t 1350 * 1351 * @param[in,out] ctx - pldm_msgbuf context for extractor 1352 * @param[out] dst - destination of extracted value 1353 * 1354 * @return 0 if buffer accesses were in-bounds, 1355 * -EINVAL if dst pointer is invalid, 1356 * -EOVERFLOW is the buffer was out of bound. 1357 */ 1358 #define pldm_msgbuf_extract_uint8_to_size(ctx, dst) \ 1359 pldm__msgbuf_extract_uint8_to_size(ctx, &(dst)) 1360 LIBPLDM_CC_NONNULL 1361 LIBPLDM_CC_ALWAYS_INLINE int 1362 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 1363 pldm__msgbuf_extract_uint8_to_size(struct pldm_msgbuf *ctx, size_t *dst) 1364 { 1365 uint8_t value; 1366 int rc; 1367 1368 rc = pldm__msgbuf_extract_uint8(ctx, &value); 1369 if (rc) { 1370 return rc; 1371 } 1372 1373 static_assert(SIZE_MAX >= UINT8_MAX, "Invalid promotion"); 1374 1375 *dst = value; 1376 return 0; 1377 } 1378 1379 /** 1380 * @brief pldm_msgbuf uint16_t extractor for a size_t 1381 * 1382 * @param[in,out] ctx - pldm_msgbuf context for extractor 1383 * @param[out] dst - destination of extracted value 1384 * 1385 * @return 0 if buffer accesses were in-bounds, 1386 * -EINVAL if dst pointer is invalid, 1387 * -EOVERFLOW is the buffer was out of bound. 1388 */ 1389 #define pldm_msgbuf_extract_uint16_to_size(ctx, dst) \ 1390 pldm__msgbuf_extract_uint16_to_size(ctx, &(dst)) 1391 LIBPLDM_CC_NONNULL 1392 LIBPLDM_CC_ALWAYS_INLINE int 1393 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 1394 pldm__msgbuf_extract_uint16_to_size(struct pldm_msgbuf *ctx, size_t *dst) 1395 { 1396 uint16_t value; 1397 int rc; 1398 1399 rc = pldm__msgbuf_extract_uint16(ctx, &value); 1400 if (rc) { 1401 return rc; 1402 } 1403 1404 static_assert(SIZE_MAX >= UINT16_MAX, "Invalid promotion"); 1405 1406 *dst = value; 1407 return 0; 1408 } 1409 1410 /** 1411 * @brief pldm_msgbuf uint32_t extractor for a size_t 1412 * 1413 * @param[in,out] ctx - pldm_msgbuf context for extractor 1414 * @param[out] dst - destination of extracted value 1415 * 1416 * @return 0 if buffer accesses were in-bounds, 1417 * -EINVAL if dst pointer is invalid, 1418 * -EOVERFLOW is the buffer was out of bound. 1419 */ 1420 #define pldm_msgbuf_extract_uint32_to_size(ctx, dst) \ 1421 pldm__msgbuf_extract_uint32_to_size(ctx, &(dst)) 1422 LIBPLDM_CC_NONNULL 1423 LIBPLDM_CC_ALWAYS_INLINE int 1424 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 1425 pldm__msgbuf_extract_uint32_to_size(struct pldm_msgbuf *ctx, size_t *dst) 1426 { 1427 uint32_t value; 1428 int rc; 1429 1430 rc = pldm__msgbuf_extract_uint32(ctx, &value); 1431 if (rc) { 1432 return rc; 1433 } 1434 1435 static_assert(SIZE_MAX >= UINT32_MAX, "Invalid promotion"); 1436 1437 *dst = value; 1438 return 0; 1439 } 1440 1441 #ifdef __cplusplus 1442 } 1443 #endif 1444 1445 #ifdef __cplusplus 1446 #include <type_traits> 1447 1448 template <typename T> 1449 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx, 1450 void *buf) 1451 { 1452 static_assert(std::is_same<uint8_t, T>::value); 1453 return pldm__msgbuf_extract_uint8(ctx, buf); 1454 } 1455 1456 template <typename T> 1457 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx, 1458 void *buf) 1459 { 1460 static_assert(std::is_same<int8_t, T>::value); 1461 return pldm__msgbuf_extract_int8(ctx, buf); 1462 } 1463 1464 template <typename T> 1465 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx, 1466 void *buf) 1467 { 1468 static_assert(std::is_same<uint16_t, T>::value); 1469 return pldm__msgbuf_extract_uint16(ctx, buf); 1470 } 1471 1472 template <typename T> 1473 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx, 1474 void *buf) 1475 { 1476 static_assert(std::is_same<int16_t, T>::value); 1477 return pldm__msgbuf_extract_int16(ctx, buf); 1478 } 1479 1480 template <typename T> 1481 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx, 1482 void *buf) 1483 { 1484 static_assert(std::is_same<uint32_t, T>::value); 1485 return pldm__msgbuf_extract_uint32(ctx, buf); 1486 } 1487 1488 template <typename T> 1489 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx, 1490 void *buf) 1491 { 1492 static_assert(std::is_same<int32_t, T>::value); 1493 return pldm__msgbuf_extract_int32(ctx, buf); 1494 } 1495 1496 template <typename T> 1497 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx, 1498 void *buf) 1499 { 1500 static_assert(std::is_same<real32_t, T>::value); 1501 return pldm__msgbuf_extract_real32(ctx, buf); 1502 } 1503 #endif 1504 1505 #endif /* BUF_H */ 1506