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