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 <limits.h> 50 #include <stdbool.h> 51 #include <stdint.h> 52 #include <string.h> 53 #include <sys/types.h> 54 55 struct pldm_msgbuf { 56 uint8_t *cursor; 57 ssize_t remaining; 58 }; 59 60 /** 61 * @brief Initialize pldm buf struct for buf extractor 62 * 63 * @param[out] ctx - pldm_msgbuf context for extractor 64 * @param[in] minsize - The minimum required length of buffer `buf` 65 * @param[in] buf - buffer to be extracted 66 * @param[in] len - size of buffer 67 * 68 * @return PLDM_SUCCESS if all buffer accesses were in-bounds, 69 * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or 70 * PLDM_ERROR_INVALID_LENGTH if length constraints are violated. 71 */ 72 __attribute__((no_sanitize("pointer-overflow"))) static inline int 73 pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf, 74 size_t len) 75 { 76 uint8_t *end; 77 78 if (!ctx || !buf) { 79 return PLDM_ERROR_INVALID_DATA; 80 } 81 82 if ((minsize > len) || (len > SSIZE_MAX)) { 83 return PLDM_ERROR_INVALID_LENGTH; 84 } 85 86 end = (uint8_t *)buf + len; 87 if (end && end < (uint8_t *)buf) { 88 return PLDM_ERROR_INVALID_LENGTH; 89 } 90 91 ctx->cursor = (uint8_t *)buf; 92 ctx->remaining = (ssize_t)len; 93 94 return PLDM_SUCCESS; 95 } 96 97 /** 98 * @brief Validate buffer overflow state 99 * 100 * @param[in] ctx - pldm_msgbuf context for extractor 101 * 102 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain 103 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a 104 * prior accesses would have occurred beyond the bounds of the buffer, and 105 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 106 * pointer. 107 */ 108 static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx) 109 { 110 if (!ctx) { 111 return PLDM_ERROR_INVALID_DATA; 112 } 113 114 return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH; 115 } 116 117 /** 118 * @brief Test whether a message buffer has been exactly consumed 119 * 120 * @param[in] ctx - pldm_msgbuf context for extractor 121 * 122 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from 123 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH 124 * indicates that an incorrect sequence of accesses have occurred, and 125 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 126 * pointer. 127 */ 128 static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx) 129 { 130 if (!ctx) { 131 return PLDM_ERROR_INVALID_DATA; 132 } 133 134 return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH; 135 } 136 137 /** 138 * @brief Destroy the pldm buf 139 * 140 * @param[in] ctx - pldm_msgbuf context for extractor 141 * 142 * @return PLDM_SUCCESS if all buffer accesses were in-bounds, 143 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or 144 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the 145 * bounds of the buffer. 146 */ 147 static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx) 148 { 149 int valid; 150 151 if (!ctx) { 152 return PLDM_ERROR_INVALID_DATA; 153 } 154 155 valid = pldm_msgbuf_validate(ctx); 156 157 ctx->cursor = NULL; 158 ctx->remaining = 0; 159 160 return valid; 161 } 162 163 /** 164 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer 165 * has been completely consumed without overflow 166 * 167 * @param[in] ctx - pldm_msgbuf context 168 * 169 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely 170 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx 171 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would 172 * have occurred byond the bounds of the buffer 173 */ 174 static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx) 175 { 176 int consumed; 177 178 if (!ctx) { 179 return PLDM_ERROR_INVALID_DATA; 180 } 181 182 consumed = pldm_msgbuf_consumed(ctx); 183 184 ctx->cursor = NULL; 185 ctx->remaining = 0; 186 187 return consumed; 188 } 189 190 /* 191 * Exploit the pre-processor to perform type checking by macro substitution. 192 * 193 * A C type is defined by its alignment as well as its object 194 * size, and compilers have a hammer to enforce it in the form of 195 * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in 196 * the libpldm public API this presents a problem: Naively attempting to use the 197 * msgbuf APIs on a member of a packed struct would yield an error. 198 * 199 * The msgbuf APIs are implemented such that data is moved through unaligned 200 * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must 201 * make the object pointers take a trip through `void *` at its API boundary. 202 * That presents a bit too much of an opportunity to non-surgically remove your 203 * own foot, so here we set about doing something to mitigate that as well. 204 * 205 * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness 206 * only for the purpose of object sizes, disregarding alignment. We have a few 207 * constraints that cause some headaches: 208 * 209 * 1. We have to perform the type-check before a call through a C function, 210 * as the function must take the object pointer argument as `void *`. 211 * Essentially, this constrains us to doing something with macros. 212 * 213 * 2. While libpldm is a C library, its test suite is written in C++ to take 214 * advantage of gtest. 215 * 216 * 3. Ideally we'd do something with C's `static_assert()`, however 217 * `static_assert()` is defined as void, and as we're constrained to macros, 218 * using `static_assert()` would require a statement-expression 219 * 220 * 4. Currently the project is built with `-std=c17`. CPP statement-expressions 221 * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for 222 * the purpose of enabling statement-expressions in this one instance. 223 * 224 * 5. We can achieve a conditional build error using `pldm_require_obj_type()`, 225 * however it's implemented in terms of `_Generic()`, which is not available 226 * in C++. 227 * 228 * Combined this means we need separate solutions for C and C++. 229 * 230 * For C, as we don't have statement-expressions, we need to exploit some other 231 * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf 232 * API function call. We also have to take care of the fact that the call-sites 233 * may be in the context of a variable assignment for error-handling purposes. 234 * The key observation is that we can use the comma operator as a sequence point 235 * to order the type check before the API call, discarding the "result" value of 236 * the type check and yielding the return value of the API call. 237 * 238 * C++ could be less of a headache than the C as we can leverage template 239 * functions. An advantage of template functions is that while their definition 240 * is driven by instantion, the definition does not appear at the source 241 * location of the instantation, which gives it a great leg-up over the problems 242 * we have in the C path. However, the use of the msgbuf APIs in the test suite 243 * still makes things somewhat tricky, as the call-sites in the test suite are 244 * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that 245 * takes both the object type and the required type as template arguments, and 246 * then define the object pointer parameter as `void *` for a call through to 247 * the appropriate msgbuf API. However, because the msgbuf API call-sites are 248 * encapsulated in gtest macros, use of commas in the template specification 249 * causes pre-processor confusion. In this way we're constrained to only one 250 * template argument per function. 251 * 252 * Implement the C++ path using template functions that take the destination 253 * object type as a template argument, while the name of the function symbols 254 * are derived from the required type. The manual implementations of these 255 * appear at the end of the header. The type safety is actually enforced 256 * by `static_assert()` this time, as we can use statements as we're not 257 * constrained to an expression in the templated function body. 258 * 259 * The invocations of pldm_msgbuf_extract_typecheck() typically result in 260 * double-evaluation of some arguments. We're not yet bothered by this for two 261 * reasons: 262 * 263 * 1. The nature of the current call-sites are such that there are no 264 * argument expressions that result in undesirable side-effects 265 * 266 * 2. It's an API internal to the libpldm implementation, and we can fix things 267 * whenever something crops up the violates the observation in 1. 268 */ 269 #ifdef __cplusplus 270 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \ 271 pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__) 272 #else 273 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \ 274 (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__)) 275 #endif 276 277 /** 278 * @brief pldm_msgbuf extractor for a uint8_t 279 * 280 * @param[inout] ctx - pldm_msgbuf context for extractor 281 * @param[out] dst - destination of extracted value 282 * 283 * @return PLDM_SUCCESS if buffer accesses were in-bounds, 284 * PLDM_ERROR_INVALID_LENGTH otherwise. 285 * PLDM_ERROR_INVALID_DATA if input a invalid ctx 286 */ 287 #define pldm_msgbuf_extract_uint8(ctx, dst) \ 288 pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \ 289 dst, ctx, dst) 290 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 291 static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst) 292 { 293 if (!ctx || !ctx->cursor || !dst) { 294 return PLDM_ERROR_INVALID_DATA; 295 } 296 297 ctx->remaining -= sizeof(uint8_t); 298 assert(ctx->remaining >= 0); 299 if (ctx->remaining < 0) { 300 return PLDM_ERROR_INVALID_LENGTH; 301 } 302 303 memcpy(dst, ctx->cursor, sizeof(uint8_t)); 304 305 ctx->cursor++; 306 return PLDM_SUCCESS; 307 } 308 309 #define pldm_msgbuf_extract_int8(ctx, dst) \ 310 pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \ 311 ctx, dst) 312 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 313 static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst) 314 { 315 if (!ctx || !ctx->cursor || !dst) { 316 return PLDM_ERROR_INVALID_DATA; 317 } 318 319 ctx->remaining -= sizeof(int8_t); 320 assert(ctx->remaining >= 0); 321 if (ctx->remaining < 0) { 322 return PLDM_ERROR_INVALID_LENGTH; 323 } 324 325 memcpy(dst, ctx->cursor, sizeof(int8_t)); 326 ctx->cursor++; 327 return PLDM_SUCCESS; 328 } 329 330 #define pldm_msgbuf_extract_uint16(ctx, dst) \ 331 pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \ 332 dst, ctx, dst) 333 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 334 static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, 335 void *dst) 336 { 337 uint16_t ldst; 338 339 if (!ctx || !ctx->cursor || !dst) { 340 return PLDM_ERROR_INVALID_DATA; 341 } 342 343 // Check for buffer overflow. If we overflow, account for the request as 344 // negative values in ctx->remaining. This way we can debug how far 345 // we've overflowed. 346 ctx->remaining -= sizeof(ldst); 347 348 // Prevent the access if it would overflow. First, assert so we blow up 349 // the test suite right at the point of failure. However, cater to 350 // -DNDEBUG by explicitly testing that the access is valid. 351 assert(ctx->remaining >= 0); 352 if (ctx->remaining < 0) { 353 return PLDM_ERROR_INVALID_LENGTH; 354 } 355 356 // Use memcpy() to have the compiler deal with any alignment 357 // issues on the target architecture 358 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 359 360 // Only assign the target value once it's correctly decoded 361 ldst = le16toh(ldst); 362 363 // Allow storing to unaligned 364 memcpy(dst, &ldst, sizeof(ldst)); 365 366 ctx->cursor += sizeof(ldst); 367 368 return PLDM_SUCCESS; 369 } 370 371 #define pldm_msgbuf_extract_int16(ctx, dst) \ 372 pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \ 373 dst, ctx, dst) 374 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 375 static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst) 376 { 377 int16_t ldst; 378 379 if (!ctx || !ctx->cursor || !dst) { 380 return PLDM_ERROR_INVALID_DATA; 381 } 382 383 ctx->remaining -= sizeof(ldst); 384 assert(ctx->remaining >= 0); 385 if (ctx->remaining < 0) { 386 return PLDM_ERROR_INVALID_LENGTH; 387 } 388 389 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 390 391 ldst = le16toh(ldst); 392 memcpy(dst, &ldst, sizeof(ldst)); 393 ctx->cursor += sizeof(ldst); 394 395 return PLDM_SUCCESS; 396 } 397 398 #define pldm_msgbuf_extract_uint32(ctx, dst) \ 399 pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \ 400 dst, ctx, dst) 401 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 402 static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, 403 void *dst) 404 { 405 uint32_t ldst; 406 407 if (!ctx || !ctx->cursor || !dst) { 408 return PLDM_ERROR_INVALID_DATA; 409 } 410 411 ctx->remaining -= sizeof(ldst); 412 assert(ctx->remaining >= 0); 413 if (ctx->remaining < 0) { 414 return PLDM_ERROR_INVALID_LENGTH; 415 } 416 417 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 418 419 ldst = le32toh(ldst); 420 memcpy(dst, &ldst, sizeof(ldst)); 421 ctx->cursor += sizeof(ldst); 422 423 return PLDM_SUCCESS; 424 } 425 426 #define pldm_msgbuf_extract_int32(ctx, dst) \ 427 pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \ 428 dst, ctx, dst) 429 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 430 static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst) 431 { 432 int32_t ldst; 433 434 if (!ctx || !ctx->cursor || !dst) { 435 return PLDM_ERROR_INVALID_DATA; 436 } 437 438 ctx->remaining -= sizeof(ldst); 439 assert(ctx->remaining >= 0); 440 if (ctx->remaining < 0) { 441 return PLDM_ERROR_INVALID_LENGTH; 442 } 443 444 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 445 446 ldst = le32toh(ldst); 447 memcpy(dst, &ldst, sizeof(ldst)); 448 ctx->cursor += sizeof(ldst); 449 450 return PLDM_SUCCESS; 451 } 452 453 #define pldm_msgbuf_extract_real32(ctx, dst) \ 454 pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \ 455 dst, ctx, dst) 456 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 457 static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, 458 void *dst) 459 { 460 uint32_t ldst; 461 462 _Static_assert(sizeof(real32_t) == sizeof(ldst), 463 "Mismatched type sizes for dst and ldst"); 464 465 if (!ctx || !ctx->cursor || !dst) { 466 return PLDM_ERROR_INVALID_DATA; 467 } 468 469 ctx->remaining -= sizeof(ldst); 470 assert(ctx->remaining >= 0); 471 if (ctx->remaining < 0) { 472 return PLDM_ERROR_INVALID_LENGTH; 473 } 474 475 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 476 ldst = le32toh(ldst); 477 memcpy(dst, &ldst, sizeof(ldst)); 478 ctx->cursor += sizeof(ldst); 479 480 return PLDM_SUCCESS; 481 } 482 483 /** 484 * Extract the field at the msgbuf cursor into the lvalue named by dst. 485 * 486 * @param ctx The msgbuf context object 487 * @param dst The lvalue into which the field at the msgbuf cursor should be 488 * extracted 489 * 490 * @return PLDM_SUCCESS on success, otherwise another value on error 491 */ 492 #define pldm_msgbuf_extract(ctx, dst) \ 493 _Generic((dst), \ 494 uint8_t: pldm__msgbuf_extract_uint8, \ 495 int8_t: pldm__msgbuf_extract_int8, \ 496 uint16_t: pldm__msgbuf_extract_uint16, \ 497 int16_t: pldm__msgbuf_extract_int16, \ 498 uint32_t: pldm__msgbuf_extract_uint32, \ 499 int32_t: pldm__msgbuf_extract_int32, \ 500 real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst)) 501 502 /** 503 * Extract the field at the msgbuf cursor into the object pointed-to by dst. 504 * 505 * @param ctx The msgbuf context object 506 * @param dst The pointer to the object into which the field at the msgbuf 507 * cursor should be extracted 508 * 509 * @return PLDM_SUCCESS on success, otherwise another value on error 510 */ 511 #define pldm_msgbuf_extract_p(ctx, dst) \ 512 _Generic((dst), \ 513 uint8_t *: pldm__msgbuf_extract_uint8, \ 514 int8_t *: pldm__msgbuf_extract_int8, \ 515 uint16_t *: pldm__msgbuf_extract_uint16, \ 516 int16_t *: pldm__msgbuf_extract_int16, \ 517 uint32_t *: pldm__msgbuf_extract_uint32, \ 518 int32_t *: pldm__msgbuf_extract_int32, \ 519 real32_t *: pldm__msgbuf_extract_real32)(ctx, dst) 520 521 static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, 522 uint8_t *dst, size_t count) 523 { 524 if (!ctx || !ctx->cursor || !dst) { 525 return PLDM_ERROR_INVALID_DATA; 526 } 527 528 if (!count) { 529 return PLDM_SUCCESS; 530 } 531 532 if (count >= SSIZE_MAX) { 533 return PLDM_ERROR_INVALID_LENGTH; 534 } 535 536 ctx->remaining -= (ssize_t)count; 537 assert(ctx->remaining >= 0); 538 if (ctx->remaining < 0) { 539 return PLDM_ERROR_INVALID_LENGTH; 540 } 541 542 memcpy(dst, ctx->cursor, count); 543 ctx->cursor += count; 544 545 return PLDM_SUCCESS; 546 } 547 548 #define pldm_msgbuf_extract_array(ctx, dst, count) \ 549 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \ 550 count) 551 552 static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, 553 const uint32_t src) 554 { 555 uint32_t val = htole32(src); 556 557 if (!ctx || !ctx->cursor) { 558 return PLDM_ERROR_INVALID_DATA; 559 } 560 561 ctx->remaining -= sizeof(src); 562 assert(ctx->remaining >= 0); 563 if (ctx->remaining < 0) { 564 return PLDM_ERROR_INVALID_LENGTH; 565 } 566 567 memcpy(ctx->cursor, &val, sizeof(val)); 568 ctx->cursor += sizeof(src); 569 570 return PLDM_SUCCESS; 571 } 572 573 static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, 574 const uint16_t src) 575 { 576 uint16_t val = htole16(src); 577 578 if (!ctx || !ctx->cursor) { 579 return PLDM_ERROR_INVALID_DATA; 580 } 581 582 ctx->remaining -= sizeof(src); 583 assert(ctx->remaining >= 0); 584 if (ctx->remaining < 0) { 585 return PLDM_ERROR_INVALID_LENGTH; 586 } 587 588 memcpy(ctx->cursor, &val, sizeof(val)); 589 ctx->cursor += sizeof(src); 590 591 return PLDM_SUCCESS; 592 } 593 594 static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, 595 const uint8_t src) 596 { 597 if (!ctx || !ctx->cursor) { 598 return PLDM_ERROR_INVALID_DATA; 599 } 600 601 ctx->remaining -= sizeof(src); 602 assert(ctx->remaining >= 0); 603 if (ctx->remaining < 0) { 604 return PLDM_ERROR_INVALID_LENGTH; 605 } 606 607 memcpy(ctx->cursor, &src, sizeof(src)); 608 ctx->cursor += sizeof(src); 609 610 return PLDM_SUCCESS; 611 } 612 613 static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, 614 const int32_t src) 615 { 616 int32_t val = htole32(src); 617 618 if (!ctx || !ctx->cursor) { 619 return PLDM_ERROR_INVALID_DATA; 620 } 621 622 ctx->remaining -= sizeof(src); 623 assert(ctx->remaining >= 0); 624 if (ctx->remaining < 0) { 625 return PLDM_ERROR_INVALID_LENGTH; 626 } 627 628 memcpy(ctx->cursor, &val, sizeof(val)); 629 ctx->cursor += sizeof(src); 630 631 return PLDM_SUCCESS; 632 } 633 634 static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, 635 const int16_t src) 636 { 637 int16_t val = htole16(src); 638 639 if (!ctx || !ctx->cursor) { 640 return PLDM_ERROR_INVALID_DATA; 641 } 642 643 ctx->remaining -= sizeof(src); 644 assert(ctx->remaining >= 0); 645 if (ctx->remaining < 0) { 646 return PLDM_ERROR_INVALID_LENGTH; 647 } 648 649 memcpy(ctx->cursor, &val, sizeof(val)); 650 ctx->cursor += sizeof(src); 651 652 return PLDM_SUCCESS; 653 } 654 655 static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, 656 const int8_t src) 657 { 658 if (!ctx || !ctx->cursor) { 659 return PLDM_ERROR_INVALID_DATA; 660 } 661 662 ctx->remaining -= sizeof(src); 663 assert(ctx->remaining >= 0); 664 if (ctx->remaining < 0) { 665 return PLDM_ERROR_INVALID_LENGTH; 666 } 667 668 memcpy(ctx->cursor, &src, sizeof(src)); 669 ctx->cursor += sizeof(src); 670 671 return PLDM_SUCCESS; 672 } 673 674 #define pldm_msgbuf_insert(dst, src) \ 675 _Generic((src), \ 676 uint8_t: pldm_msgbuf_insert_uint8, \ 677 int8_t: pldm_msgbuf_insert_int8, \ 678 uint16_t: pldm_msgbuf_insert_uint16, \ 679 int16_t: pldm_msgbuf_insert_int16, \ 680 uint32_t: pldm_msgbuf_insert_uint32, \ 681 int32_t: pldm_msgbuf_insert_int32)(dst, src) 682 683 static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, 684 const uint8_t *src, 685 size_t count) 686 { 687 if (!ctx || !ctx->cursor || !src) { 688 return PLDM_ERROR_INVALID_DATA; 689 } 690 691 if (!count) { 692 return PLDM_SUCCESS; 693 } 694 695 if (count >= SSIZE_MAX) { 696 return PLDM_ERROR_INVALID_LENGTH; 697 } 698 699 ctx->remaining -= (ssize_t)count; 700 assert(ctx->remaining >= 0); 701 if (ctx->remaining < 0) { 702 return PLDM_ERROR_INVALID_LENGTH; 703 } 704 705 memcpy(ctx->cursor, src, count); 706 ctx->cursor += count; 707 708 return PLDM_SUCCESS; 709 } 710 711 #define pldm_msgbuf_insert_array(dst, src, count) \ 712 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \ 713 count) 714 715 static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, 716 size_t required, void **cursor) 717 { 718 if (!ctx || !ctx->cursor || !cursor || *cursor) { 719 return PLDM_ERROR_INVALID_DATA; 720 } 721 722 if (required > SSIZE_MAX) { 723 return PLDM_ERROR_INVALID_LENGTH; 724 } 725 726 ctx->remaining -= (ssize_t)required; 727 assert(ctx->remaining >= 0); 728 if (ctx->remaining < 0) { 729 return PLDM_ERROR_INVALID_LENGTH; 730 } 731 732 *cursor = ctx->cursor; 733 ctx->cursor += required; 734 735 return PLDM_SUCCESS; 736 } 737 738 static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, 739 void **cursor, size_t *len) 740 { 741 if (!ctx || !ctx->cursor || !cursor || *cursor || !len) { 742 return PLDM_ERROR_INVALID_DATA; 743 } 744 745 assert(ctx->remaining >= 0); 746 if (ctx->remaining < 0) { 747 return PLDM_ERROR_INVALID_LENGTH; 748 } 749 750 *cursor = ctx->cursor; 751 ctx->cursor += ctx->remaining; 752 *len = ctx->remaining; 753 ctx->remaining = 0; 754 755 return PLDM_SUCCESS; 756 } 757 #ifdef __cplusplus 758 } 759 #endif 760 761 #ifdef __cplusplus 762 #include <type_traits> 763 764 template <typename T> 765 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx, 766 void *buf) 767 { 768 static_assert(std::is_same<uint8_t *, T>::value); 769 return pldm__msgbuf_extract_uint8(ctx, buf); 770 } 771 772 template <typename T> 773 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx, 774 void *buf) 775 { 776 static_assert(std::is_same<int8_t *, T>::value); 777 return pldm__msgbuf_extract_int8(ctx, buf); 778 } 779 780 template <typename T> 781 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx, 782 void *buf) 783 { 784 static_assert(std::is_same<uint16_t *, T>::value); 785 return pldm__msgbuf_extract_uint16(ctx, buf); 786 } 787 788 template <typename T> 789 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx, 790 void *buf) 791 { 792 static_assert(std::is_same<int16_t *, T>::value); 793 return pldm__msgbuf_extract_int16(ctx, buf); 794 } 795 796 template <typename T> 797 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx, 798 void *buf) 799 { 800 static_assert(std::is_same<uint32_t *, T>::value); 801 return pldm__msgbuf_extract_uint32(ctx, buf); 802 } 803 804 template <typename T> 805 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx, 806 void *buf) 807 { 808 static_assert(std::is_same<int32_t *, T>::value); 809 return pldm__msgbuf_extract_int32(ctx, buf); 810 } 811 812 template <typename T> 813 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx, 814 void *buf) 815 { 816 static_assert(std::is_same<real32_t *, T>::value); 817 return pldm__msgbuf_extract_real32(ctx, buf); 818 } 819 #endif 820 821 #endif /* BUF_H */ 822