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