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