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