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