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