1 #ifndef PLDM_MSGBUF_H 2 #define PLDM_MSGBUF_H 3 4 #ifdef __cplusplus 5 /* 6 * Fix up C11's _Static_assert() vs C++'s static_assert(). 7 * 8 * Can we please have nice things for once. 9 */ 10 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 11 #define _Static_assert(...) static_assert(__VA_ARGS__) 12 extern "C" { 13 #endif 14 15 #include "base.h" 16 #include "pldm_types.h" 17 18 #include <assert.h> 19 #include <endian.h> 20 #include <limits.h> 21 #include <stdbool.h> 22 #include <string.h> 23 #include <sys/types.h> 24 25 /* 26 * Fix up C11's _Static_assert() vs C++'s static_assert(). 27 * 28 * Can we please have nice things for once. 29 */ 30 #ifdef __cplusplus 31 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 32 #define _Static_assert(...) static_assert(__VA_ARGS__) 33 #endif 34 35 struct pldm_msgbuf { 36 uint8_t *cursor; 37 ssize_t remaining; 38 }; 39 40 /** 41 * @brief Initialize pldm buf struct for buf extractor 42 * 43 * @param[out] ctx - pldm_msgbuf context for extractor 44 * @param[in] minsize - The minimum required length of buffer `buf` 45 * @param[in] buf - buffer to be extracted 46 * @param[in] len - size of buffer 47 * 48 * @return PLDM_SUCCESS if all buffer accesses were in-bounds, 49 * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or 50 * PLDM_ERROR_INVALID_LENGTH if length constraints are violated. 51 */ 52 __attribute__((no_sanitize("pointer-overflow"))) static inline int 53 pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf, 54 size_t len) 55 { 56 uint8_t *end; 57 58 if (!ctx || !buf) { 59 return PLDM_ERROR_INVALID_DATA; 60 } 61 62 if ((minsize > len) || (len > SSIZE_MAX)) { 63 return PLDM_ERROR_INVALID_LENGTH; 64 } 65 66 end = (uint8_t *)buf + len; 67 if (end && end < (uint8_t *)buf) { 68 return PLDM_ERROR_INVALID_LENGTH; 69 } 70 71 ctx->cursor = (uint8_t *)buf; 72 ctx->remaining = (ssize_t)len; 73 74 return PLDM_SUCCESS; 75 } 76 77 /** 78 * @brief Validate buffer overflow state 79 * 80 * @param[in] ctx - pldm_msgbuf context for extractor 81 * 82 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain 83 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a 84 * prior accesses would have occurred beyond the bounds of the buffer, and 85 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 86 * pointer. 87 */ 88 static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx) 89 { 90 if (!ctx) { 91 return PLDM_ERROR_INVALID_DATA; 92 } 93 94 return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH; 95 } 96 97 /** 98 * @brief Test whether a message buffer has been exactly consumed 99 * 100 * @param[in] ctx - pldm_msgbuf context for extractor 101 * 102 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from 103 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH 104 * indicates that an incorrect sequence of accesses have occurred, and 105 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 106 * pointer. 107 */ 108 static inline int pldm_msgbuf_consumed(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 Destroy the pldm buf 119 * 120 * @param[in] ctx - pldm_msgbuf context for extractor 121 * 122 * @return PLDM_SUCCESS if all buffer accesses were in-bounds, 123 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or 124 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the 125 * bounds of the buffer. 126 */ 127 static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx) 128 { 129 int valid; 130 131 if (!ctx) { 132 return PLDM_ERROR_INVALID_DATA; 133 } 134 135 valid = pldm_msgbuf_validate(ctx); 136 137 ctx->cursor = NULL; 138 ctx->remaining = 0; 139 140 return valid; 141 } 142 143 /** 144 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer 145 * has been completely consumed without overflow 146 * 147 * @param[in] ctx - pldm_msgbuf context 148 * 149 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely 150 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx 151 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would 152 * have occurred byond the bounds of the buffer 153 */ 154 static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx) 155 { 156 int consumed; 157 158 if (!ctx) { 159 return PLDM_ERROR_INVALID_DATA; 160 } 161 162 consumed = pldm_msgbuf_consumed(ctx); 163 164 ctx->cursor = NULL; 165 ctx->remaining = 0; 166 167 return consumed; 168 } 169 170 /** 171 * @brief pldm_msgbuf extractor for a uint8_t 172 * 173 * @param[inout] ctx - pldm_msgbuf context for extractor 174 * @param[out] dst - destination of extracted value 175 * 176 * @return PLDM_SUCCESS if buffer accesses were in-bounds, 177 * PLDM_ERROR_INVALID_LENGTH otherwise. 178 * PLDM_ERROR_INVALID_DATA if input a invalid ctx 179 */ 180 static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx, 181 uint8_t *dst) 182 { 183 if (!ctx || !ctx->cursor || !dst) { 184 return PLDM_ERROR_INVALID_DATA; 185 } 186 187 ctx->remaining -= sizeof(*dst); 188 assert(ctx->remaining >= 0); 189 if (ctx->remaining < 0) { 190 return PLDM_ERROR_INVALID_LENGTH; 191 } 192 193 *dst = *((uint8_t *)(ctx->cursor)); 194 ctx->cursor++; 195 return PLDM_SUCCESS; 196 } 197 198 static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_t *dst) 199 { 200 if (!ctx || !ctx->cursor || !dst) { 201 return PLDM_ERROR_INVALID_DATA; 202 } 203 204 ctx->remaining -= sizeof(*dst); 205 assert(ctx->remaining >= 0); 206 if (ctx->remaining < 0) { 207 return PLDM_ERROR_INVALID_LENGTH; 208 } 209 210 *dst = *((int8_t *)(ctx->cursor)); 211 ctx->cursor++; 212 return PLDM_SUCCESS; 213 } 214 215 static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx, 216 uint16_t *dst) 217 { 218 uint16_t ldst; 219 220 if (!ctx || !ctx->cursor || !dst) { 221 return PLDM_ERROR_INVALID_DATA; 222 } 223 224 // Check for buffer overflow. If we overflow, account for the request as 225 // negative values in ctx->remaining. This way we can debug how far 226 // we've overflowed. 227 ctx->remaining -= sizeof(ldst); 228 229 // Prevent the access if it would overflow. First, assert so we blow up 230 // the test suite right at the point of failure. However, cater to 231 // -DNDEBUG by explicitly testing that the access is valid. 232 assert(ctx->remaining >= 0); 233 if (ctx->remaining < 0) { 234 return PLDM_ERROR_INVALID_LENGTH; 235 } 236 237 // Use memcpy() to have the compiler deal with any alignment 238 // issues on the target architecture 239 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 240 241 // Only assign the target value once it's correctly decoded 242 *dst = le16toh(ldst); 243 ctx->cursor += sizeof(ldst); 244 245 return PLDM_SUCCESS; 246 } 247 248 static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx, 249 int16_t *dst) 250 { 251 int16_t ldst; 252 253 if (!ctx || !ctx->cursor || !dst) { 254 return PLDM_ERROR_INVALID_DATA; 255 } 256 257 ctx->remaining -= sizeof(ldst); 258 assert(ctx->remaining >= 0); 259 if (ctx->remaining < 0) { 260 return PLDM_ERROR_INVALID_LENGTH; 261 } 262 263 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 264 265 *dst = le16toh(ldst); 266 ctx->cursor += sizeof(ldst); 267 268 return PLDM_SUCCESS; 269 } 270 271 static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx, 272 uint32_t *dst) 273 { 274 uint32_t ldst; 275 276 if (!ctx || !ctx->cursor || !dst) { 277 return PLDM_ERROR_INVALID_DATA; 278 } 279 280 ctx->remaining -= sizeof(ldst); 281 assert(ctx->remaining >= 0); 282 if (ctx->remaining < 0) { 283 return PLDM_ERROR_INVALID_LENGTH; 284 } 285 286 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 287 288 *dst = le32toh(ldst); 289 ctx->cursor += sizeof(ldst); 290 291 return PLDM_SUCCESS; 292 } 293 294 static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx, 295 int32_t *dst) 296 { 297 int32_t ldst; 298 299 if (!ctx || !ctx->cursor || !dst) { 300 return PLDM_ERROR_INVALID_DATA; 301 } 302 303 ctx->remaining -= sizeof(ldst); 304 assert(ctx->remaining >= 0); 305 if (ctx->remaining < 0) { 306 return PLDM_ERROR_INVALID_LENGTH; 307 } 308 309 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 310 311 *dst = le32toh(ldst); 312 ctx->cursor += sizeof(ldst); 313 314 return PLDM_SUCCESS; 315 } 316 317 static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx, 318 real32_t *dst) 319 { 320 uint32_t ldst; 321 322 if (!ctx || !ctx->cursor || !dst) { 323 return PLDM_ERROR_INVALID_DATA; 324 } 325 326 ctx->remaining -= sizeof(ldst); 327 assert(ctx->remaining >= 0); 328 if (ctx->remaining < 0) { 329 return PLDM_ERROR_INVALID_LENGTH; 330 } 331 332 _Static_assert(sizeof(*dst) == sizeof(ldst), 333 "Mismatched type sizes for dst and ldst"); 334 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 335 ldst = le32toh(ldst); 336 memcpy(dst, &ldst, sizeof(*dst)); 337 ctx->cursor += sizeof(*dst); 338 339 return PLDM_SUCCESS; 340 } 341 342 #define pldm_msgbuf_extract(ctx, dst) \ 343 _Generic((*(dst)), \ 344 uint8_t: pldm_msgbuf_extract_uint8, \ 345 int8_t: pldm_msgbuf_extract_int8, \ 346 uint16_t: pldm_msgbuf_extract_uint16, \ 347 int16_t: pldm_msgbuf_extract_int16, \ 348 uint32_t: pldm_msgbuf_extract_uint32, \ 349 int32_t: pldm_msgbuf_extract_int32, \ 350 real32_t: pldm_msgbuf_extract_real32)(ctx, dst) 351 352 static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, 353 uint8_t *dst, size_t count) 354 { 355 size_t len; 356 357 if (!ctx || !ctx->cursor || !dst) { 358 return PLDM_ERROR_INVALID_DATA; 359 } 360 361 if (!count) { 362 return PLDM_SUCCESS; 363 } 364 365 len = sizeof(*dst) * count; 366 if (len > SSIZE_MAX) { 367 return PLDM_ERROR_INVALID_LENGTH; 368 } 369 370 ctx->remaining -= (ssize_t)len; 371 assert(ctx->remaining >= 0); 372 if (ctx->remaining < 0) { 373 return PLDM_ERROR_INVALID_LENGTH; 374 } 375 376 memcpy(dst, ctx->cursor, len); 377 ctx->cursor += len; 378 379 return PLDM_SUCCESS; 380 } 381 382 #define pldm_msgbuf_extract_array(ctx, dst, count) \ 383 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \ 384 count) 385 386 static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, 387 const uint32_t src) 388 { 389 uint32_t val = htole32(src); 390 391 if (!ctx || !ctx->cursor) { 392 return PLDM_ERROR_INVALID_DATA; 393 } 394 395 ctx->remaining -= sizeof(src); 396 assert(ctx->remaining >= 0); 397 if (ctx->remaining < 0) { 398 return PLDM_ERROR_INVALID_LENGTH; 399 } 400 401 memcpy(ctx->cursor, &val, sizeof(val)); 402 ctx->cursor += sizeof(src); 403 404 return PLDM_SUCCESS; 405 } 406 407 static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, 408 const uint16_t src) 409 { 410 uint16_t val = htole16(src); 411 412 if (!ctx || !ctx->cursor) { 413 return PLDM_ERROR_INVALID_DATA; 414 } 415 416 ctx->remaining -= sizeof(src); 417 assert(ctx->remaining >= 0); 418 if (ctx->remaining < 0) { 419 return PLDM_ERROR_INVALID_LENGTH; 420 } 421 422 memcpy(ctx->cursor, &val, sizeof(val)); 423 ctx->cursor += sizeof(src); 424 425 return PLDM_SUCCESS; 426 } 427 428 static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, 429 const uint8_t src) 430 { 431 if (!ctx || !ctx->cursor) { 432 return PLDM_ERROR_INVALID_DATA; 433 } 434 435 ctx->remaining -= sizeof(src); 436 assert(ctx->remaining >= 0); 437 if (ctx->remaining < 0) { 438 return PLDM_ERROR_INVALID_LENGTH; 439 } 440 441 memcpy(ctx->cursor, &src, sizeof(src)); 442 ctx->cursor += sizeof(src); 443 444 return PLDM_SUCCESS; 445 } 446 447 static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, 448 const int32_t src) 449 { 450 int32_t val = htole32(src); 451 452 if (!ctx || !ctx->cursor) { 453 return PLDM_ERROR_INVALID_DATA; 454 } 455 456 ctx->remaining -= sizeof(src); 457 assert(ctx->remaining >= 0); 458 if (ctx->remaining < 0) { 459 return PLDM_ERROR_INVALID_LENGTH; 460 } 461 462 memcpy(ctx->cursor, &val, sizeof(val)); 463 ctx->cursor += sizeof(src); 464 465 return PLDM_SUCCESS; 466 } 467 468 static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, 469 const int16_t src) 470 { 471 int16_t val = htole16(src); 472 473 if (!ctx || !ctx->cursor) { 474 return PLDM_ERROR_INVALID_DATA; 475 } 476 477 ctx->remaining -= sizeof(src); 478 assert(ctx->remaining >= 0); 479 if (ctx->remaining < 0) { 480 return PLDM_ERROR_INVALID_LENGTH; 481 } 482 483 memcpy(ctx->cursor, &val, sizeof(val)); 484 ctx->cursor += sizeof(src); 485 486 return PLDM_SUCCESS; 487 } 488 489 static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, 490 const int8_t src) 491 { 492 if (!ctx || !ctx->cursor) { 493 return PLDM_ERROR_INVALID_DATA; 494 } 495 496 ctx->remaining -= sizeof(src); 497 assert(ctx->remaining >= 0); 498 if (ctx->remaining < 0) { 499 return PLDM_ERROR_INVALID_LENGTH; 500 } 501 502 memcpy(ctx->cursor, &src, sizeof(src)); 503 ctx->cursor += sizeof(src); 504 505 return PLDM_SUCCESS; 506 } 507 508 #define pldm_msgbuf_insert(dst, src) \ 509 _Generic((src), \ 510 uint8_t: pldm_msgbuf_insert_uint8, \ 511 int8_t: pldm_msgbuf_insert_int8, \ 512 uint16_t: pldm_msgbuf_insert_uint16, \ 513 int16_t: pldm_msgbuf_insert_int16, \ 514 uint32_t: pldm_msgbuf_insert_uint32, \ 515 int32_t: pldm_msgbuf_insert_int32)(dst, src) 516 517 static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, 518 const uint8_t *src, 519 size_t count) 520 { 521 size_t len; 522 if (!ctx || !ctx->cursor || !src) { 523 return PLDM_ERROR_INVALID_DATA; 524 } 525 526 if (!count) { 527 return PLDM_SUCCESS; 528 } 529 530 len = sizeof(*src) * count; 531 if (len > SSIZE_MAX) { 532 return PLDM_ERROR_INVALID_LENGTH; 533 } 534 535 ctx->remaining -= (ssize_t)len; 536 assert(ctx->remaining >= 0); 537 if (ctx->remaining < 0) { 538 return PLDM_ERROR_INVALID_LENGTH; 539 } 540 541 memcpy(ctx->cursor, src, len); 542 ctx->cursor += len; 543 544 return PLDM_SUCCESS; 545 } 546 547 #define pldm_msgbuf_insert_array(dst, src, count) \ 548 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \ 549 count) 550 551 static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, 552 size_t required, void **cursor) 553 { 554 if (!ctx || !ctx->cursor || !cursor || *cursor) { 555 return PLDM_ERROR_INVALID_DATA; 556 } 557 558 if (required > SSIZE_MAX) { 559 return PLDM_ERROR_INVALID_LENGTH; 560 } 561 562 ctx->remaining -= (ssize_t)required; 563 assert(ctx->remaining >= 0); 564 if (ctx->remaining < 0) { 565 return PLDM_ERROR_INVALID_LENGTH; 566 } 567 568 *cursor = ctx->cursor; 569 ctx->cursor += required; 570 571 return PLDM_SUCCESS; 572 } 573 574 static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, 575 void **cursor, size_t *len) 576 { 577 if (!ctx || !ctx->cursor || !cursor || *cursor || !len) { 578 return PLDM_ERROR_INVALID_DATA; 579 } 580 581 assert(ctx->remaining >= 0); 582 if (ctx->remaining < 0) { 583 return PLDM_ERROR_INVALID_LENGTH; 584 } 585 586 *cursor = ctx->cursor; 587 ctx->cursor += ctx->remaining; 588 *len = ctx->remaining; 589 ctx->remaining = 0; 590 591 return PLDM_SUCCESS; 592 } 593 #ifdef __cplusplus 594 } 595 #endif 596 597 #endif /* BUF_H */ 598