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