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