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