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