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, 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, 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, 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, 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, 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, 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, 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 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) { 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 *cursor = ctx->cursor; 1084 ctx->cursor += required; 1085 1086 return 0; 1087 } 1088 1089 LIBPLDM_CC_NONNULL_ARGS(1) 1090 LIBPLDM_CC_ALWAYS_INLINE int 1091 pldm_msgbuf_span_string_ascii(struct pldm_msgbuf *ctx, void **cursor, 1092 size_t *length) 1093 { 1094 intmax_t measured; 1095 1096 if (!ctx->cursor || (cursor && *cursor)) { 1097 return pldm_msgbuf_status(ctx, EINVAL); 1098 } 1099 1100 if (ctx->remaining < 0) { 1101 /* Tracking the amount of overflow gets disturbed here */ 1102 return pldm_msgbuf_status(ctx, EOVERFLOW); 1103 } 1104 1105 measured = (intmax_t)strnlen((const char *)ctx->cursor, ctx->remaining); 1106 if (measured == ctx->remaining) { 1107 /* 1108 * We have hit the end of the buffer prior to the NUL terminator. 1109 * Optimistically, the NUL terminator was one-beyond-the-end. Setting 1110 * ctx->remaining negative ensures the `pldm_msgbuf_destroy*()` APIs also 1111 * return an error. 1112 */ 1113 ctx->remaining = -1; 1114 return pldm_msgbuf_status(ctx, EOVERFLOW); 1115 } 1116 1117 /* Include the NUL terminator in the span length, as spans are opaque */ 1118 measured++; 1119 1120 if (ctx->remaining < INTMAX_MIN + measured) { 1121 return pldm_msgbuf_status(ctx, EOVERFLOW); 1122 } 1123 1124 ctx->remaining -= measured; 1125 assert(ctx->remaining >= 0); 1126 if (ctx->remaining < 0) { 1127 return pldm_msgbuf_status(ctx, EOVERFLOW); 1128 } 1129 1130 if (cursor) { 1131 *cursor = ctx->cursor; 1132 } 1133 1134 ctx->cursor += measured; 1135 1136 if (length) { 1137 *length = measured; 1138 } 1139 1140 return 0; 1141 } 1142 1143 LIBPLDM_CC_NONNULL_ARGS(1) 1144 LIBPLDM_CC_ALWAYS_INLINE int 1145 pldm_msgbuf_span_string_utf16(struct pldm_msgbuf *ctx, void **cursor, 1146 size_t *length) 1147 { 1148 static const char16_t term = 0; 1149 ptrdiff_t measured; 1150 void *end; 1151 1152 if (!ctx->cursor || (cursor && *cursor)) { 1153 return pldm_msgbuf_status(ctx, EINVAL); 1154 } 1155 1156 if (ctx->remaining < 0) { 1157 /* Tracking the amount of overflow gets disturbed here */ 1158 return pldm_msgbuf_status(ctx, EOVERFLOW); 1159 } 1160 1161 /* 1162 * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do 1163 * not form a UTF16 NUL _code-point_ due to alignment with respect to the 1164 * start of the string 1165 */ 1166 end = ctx->cursor; 1167 do { 1168 if (end != ctx->cursor) { 1169 /* 1170 * If we've looped we've found a relatively-unaligned NUL code-point. 1171 * Scan again from a relatively-aligned start point. 1172 */ 1173 end = (char *)end + 1; 1174 } 1175 measured = (char *)end - (char *)ctx->cursor; 1176 end = memmem(end, ctx->remaining - measured, &term, 1177 sizeof(term)); 1178 } while (end && ((uintptr_t)end & 1) != ((uintptr_t)ctx->cursor & 1)); 1179 1180 if (!end) { 1181 /* 1182 * Optimistically, the last required pattern byte was one beyond the end of 1183 * the buffer. Setting ctx->remaining negative ensures the 1184 * `pldm_msgbuf_destroy*()` APIs also return an error. 1185 */ 1186 ctx->remaining = -1; 1187 return pldm_msgbuf_status(ctx, EOVERFLOW); 1188 } 1189 1190 end = (char *)end + sizeof(char16_t); 1191 measured = (char *)end - (char *)ctx->cursor; 1192 1193 #if INTMAX_MAX < PTRDIFF_MAX 1194 if (measured >= INTMAX_MAX) { 1195 return pldm_msgbuf_status(ctx, EOVERFLOW); 1196 } 1197 #endif 1198 1199 if (ctx->remaining < INTMAX_MIN + (intmax_t)measured) { 1200 assert(ctx->remaining < 0); 1201 return pldm_msgbuf_status(ctx, EOVERFLOW); 1202 } 1203 1204 ctx->remaining -= (intmax_t)measured; 1205 assert(ctx->remaining >= 0); 1206 if (ctx->remaining < 0) { 1207 return pldm_msgbuf_status(ctx, EOVERFLOW); 1208 } 1209 1210 if (cursor) { 1211 *cursor = ctx->cursor; 1212 } 1213 1214 ctx->cursor += measured; 1215 1216 if (length) { 1217 *length = (size_t)measured; 1218 } 1219 1220 return 0; 1221 } 1222 1223 LIBPLDM_CC_NONNULL 1224 LIBPLDM_CC_ALWAYS_INLINE int 1225 pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len) 1226 { 1227 if (!ctx->cursor || *cursor) { 1228 return pldm_msgbuf_status(ctx, EINVAL); 1229 } 1230 1231 assert(ctx->remaining >= 0); 1232 if (ctx->remaining < 0) { 1233 return pldm_msgbuf_status(ctx, EOVERFLOW); 1234 } 1235 1236 *cursor = ctx->cursor; 1237 ctx->cursor += ctx->remaining; 1238 *len = ctx->remaining; 1239 ctx->remaining = 0; 1240 1241 return 0; 1242 } 1243 1244 /** 1245 * @brief pldm_msgbuf copy data between two msg buffers 1246 * 1247 * @param[in,out] src - pldm_msgbuf for source from where value should be copied 1248 * @param[in,out] dst - destination of copy from source 1249 * @param[in] size - size of data to be copied 1250 * @param[in] description - description of data copied 1251 * 1252 * @return PLDM_SUCCESS if buffer accesses were in-bounds, 1253 * PLDM_ERROR_INVALID_LENGTH otherwise. 1254 * PLDM_ERROR_INVALID_DATA if input is invalid 1255 */ 1256 #define pldm_msgbuf_copy(dst, src, type, name) \ 1257 pldm__msgbuf_copy(dst, src, sizeof(type), #name) 1258 LIBPLDM_CC_NONNULL 1259 LIBPLDM_CC_ALWAYS_INLINE int 1260 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 1261 pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size, 1262 const char *description LIBPLDM_CC_UNUSED) 1263 { 1264 assert(src->mode == dst->mode); 1265 1266 if (!src->cursor || !dst->cursor) { 1267 return pldm_msgbuf_status(dst, EINVAL); 1268 } 1269 1270 #if INTMAX_MAX < SIZE_MAX 1271 if (size > INTMAX_MAX) { 1272 return pldm_msgbuf_status(dst, EOVERFLOW); 1273 } 1274 #endif 1275 1276 if (src->remaining < INTMAX_MIN + (intmax_t)size) { 1277 return pldm_msgbuf_status(dst, EOVERFLOW); 1278 } 1279 1280 if (dst->remaining < INTMAX_MIN + (intmax_t)size) { 1281 return pldm_msgbuf_status(dst, EOVERFLOW); 1282 } 1283 1284 src->remaining -= (intmax_t)size; 1285 assert(src->remaining >= 0); 1286 if (src->remaining < 0) { 1287 return pldm_msgbuf_status(dst, EOVERFLOW); 1288 } 1289 1290 dst->remaining -= (intmax_t)size; 1291 assert(dst->remaining >= 0); 1292 if (dst->remaining < 0) { 1293 return pldm_msgbuf_status(dst, EOVERFLOW); 1294 } 1295 1296 memcpy(dst->cursor, src->cursor, size); 1297 src->cursor += size; 1298 dst->cursor += size; 1299 1300 return 0; 1301 } 1302 1303 LIBPLDM_CC_NONNULL 1304 LIBPLDM_CC_WARN_UNUSED_RESULT 1305 LIBPLDM_CC_ALWAYS_INLINE int 1306 pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src) 1307 { 1308 void *ascii = NULL; 1309 size_t len = 0; 1310 int rc; 1311 1312 rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len); 1313 if (rc < 0) { 1314 return rc; 1315 } 1316 1317 return pldm__msgbuf_insert_array_void(dst, len, ascii, len); 1318 } 1319 1320 LIBPLDM_CC_NONNULL 1321 LIBPLDM_CC_WARN_UNUSED_RESULT 1322 LIBPLDM_CC_ALWAYS_INLINE int 1323 pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src) 1324 { 1325 void *utf16 = NULL; 1326 size_t len = 0; 1327 int rc; 1328 1329 rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len); 1330 if (rc < 0) { 1331 return rc; 1332 } 1333 1334 return pldm__msgbuf_insert_array_void(dst, len, utf16, len); 1335 } 1336 1337 #ifdef __cplusplus 1338 } 1339 #endif 1340 1341 #ifdef __cplusplus 1342 #include <type_traits> 1343 1344 template <typename T> 1345 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx, 1346 void *buf) 1347 { 1348 static_assert(std::is_same<uint8_t *, T>::value); 1349 return pldm__msgbuf_extract_uint8(ctx, buf); 1350 } 1351 1352 template <typename T> 1353 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx, 1354 void *buf) 1355 { 1356 static_assert(std::is_same<int8_t *, T>::value); 1357 return pldm__msgbuf_extract_int8(ctx, buf); 1358 } 1359 1360 template <typename T> 1361 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx, 1362 void *buf) 1363 { 1364 static_assert(std::is_same<uint16_t *, T>::value); 1365 return pldm__msgbuf_extract_uint16(ctx, buf); 1366 } 1367 1368 template <typename T> 1369 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx, 1370 void *buf) 1371 { 1372 static_assert(std::is_same<int16_t *, T>::value); 1373 return pldm__msgbuf_extract_int16(ctx, buf); 1374 } 1375 1376 template <typename T> 1377 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx, 1378 void *buf) 1379 { 1380 static_assert(std::is_same<uint32_t *, T>::value); 1381 return pldm__msgbuf_extract_uint32(ctx, buf); 1382 } 1383 1384 template <typename T> 1385 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx, 1386 void *buf) 1387 { 1388 static_assert(std::is_same<int32_t *, T>::value); 1389 return pldm__msgbuf_extract_int32(ctx, buf); 1390 } 1391 1392 template <typename T> 1393 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx, 1394 void *buf) 1395 { 1396 static_assert(std::is_same<real32_t *, T>::value); 1397 return pldm__msgbuf_extract_real32(ctx, buf); 1398 } 1399 #endif 1400 1401 #endif /* BUF_H */ 1402