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 #include "compiler.h" 6 7 /* 8 * Historically, many of the structs exposed in libpldm's public headers are 9 * defined with __attribute__((packed)). This is unfortunate: it gives the 10 * impression that a wire-format buffer can be cast to the message type to make 11 * the message's fields easily accessible. As it turns out, that's not 12 * that's valid for several reasons: 13 * 14 * 1. Casting the wire-format buffer to a struct of the message type doesn't 15 * abstract the endianness of message field values 16 * 17 * 2. Some messages contain packed tagged union fields which cannot be properly 18 * described in a C struct. 19 * 20 * The msgbuf APIs exist to assist with (un)packing the wire-format in a way 21 * that is type-safe, spatially memory-safe, endian-safe, performant, and 22 * free of undefined-behaviour. Message structs that are added to the public 23 * library API should no-longer be marked __attribute__((packed)), and the 24 * implementation of their encode and decode functions must exploit the msgbuf 25 * API. 26 * 27 * However, we would like to allow implementation of codec functions in terms of 28 * msgbuf APIs even if they're decoding a message into a (historically) packed 29 * struct. Some of the complexity that follows is a consequence of the packed/ 30 * unpacked conflict. 31 */ 32 33 #ifdef __cplusplus 34 /* 35 * Fix up C11's _Static_assert() vs C++'s static_assert(). 36 * 37 * Can we please have nice things for once. 38 */ 39 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 40 #define _Static_assert(...) static_assert(__VA_ARGS__) 41 extern "C" { 42 #endif 43 44 #include <libpldm/base.h> 45 #include <libpldm/pldm_types.h> 46 47 #include "compiler.h" 48 49 #include <assert.h> 50 #include <endian.h> 51 #include <errno.h> 52 #include <limits.h> 53 #include <stdbool.h> 54 #include <stdint.h> 55 #include <string.h> 56 #include <sys/types.h> 57 #include <uchar.h> 58 59 /* 60 * We can't use static_assert() outside of some other C construct. Deal 61 * with high-level global assertions by burying them in an unused struct 62 * declaration, that has a sole member for compliance with the requirement that 63 * types must have a size. 64 */ 65 static struct { 66 static_assert( 67 INTMAX_MAX != SIZE_MAX, 68 "Extraction and insertion value comparisons may be broken"); 69 static_assert(INTMAX_MIN + INTMAX_MAX <= 0, 70 "Extraction and insertion arithmetic may be broken"); 71 int compliance; 72 } build_assertions LIBPLDM_CC_UNUSED; 73 74 struct pldm_msgbuf { 75 uint8_t *cursor; 76 intmax_t remaining; 77 }; 78 79 /** 80 * @brief Initialize pldm buf struct for buf extractor 81 * 82 * @param[out] ctx - pldm_msgbuf context for extractor 83 * @param[in] minsize - The minimum required length of buffer `buf` 84 * @param[in] buf - buffer to be extracted 85 * @param[in] len - size of buffer 86 * 87 * @return 0 on success, otherwise an error code appropriate for the current 88 * personality. 89 */ 90 LIBPLDM_CC_NONNULL 91 LIBPLDM_CC_ALWAYS_INLINE int 92 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 93 pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx, size_t minsize, const void *buf, 94 size_t len) 95 { 96 if ((minsize > len)) { 97 return -EOVERFLOW; 98 } 99 100 #if INTMAX_MAX < SIZE_MAX 101 if (len > INTMAX_MAX) { 102 return -EOVERFLOW; 103 } 104 #endif 105 106 if ((uintptr_t)buf + len < len) { 107 return -EOVERFLOW; 108 } 109 110 ctx->cursor = (uint8_t *)buf; 111 ctx->remaining = (intmax_t)len; 112 113 return 0; 114 } 115 116 /** 117 * @brief Validate buffer overflow state 118 * 119 * @param[in] ctx - pldm_msgbuf context for extractor 120 * 121 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain 122 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a 123 * prior accesses would have occurred beyond the bounds of the buffer, and 124 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 125 * pointer. 126 */ 127 LIBPLDM_CC_NONNULL 128 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_validate(struct pldm_msgbuf *ctx) 129 { 130 if (ctx->remaining < 0) { 131 return -EOVERFLOW; 132 } 133 134 return 0; 135 } 136 137 /** 138 * @brief Test whether a message buffer has been exactly consumed 139 * 140 * @param[in] ctx - pldm_msgbuf context for extractor 141 * 142 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from 143 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH 144 * indicates that an incorrect sequence of accesses have occurred, and 145 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 146 * pointer. 147 */ 148 LIBPLDM_CC_NONNULL 149 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx) 150 { 151 if (ctx->remaining != 0) { 152 return -EBADMSG; 153 } 154 155 return 0; 156 } 157 158 /** 159 * @brief Destroy the pldm buf 160 * 161 * @param[in] ctx - pldm_msgbuf context for extractor 162 * 163 * @return PLDM_SUCCESS if all buffer accesses were in-bounds, 164 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or 165 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the 166 * bounds of the buffer. 167 */ 168 LIBPLDM_CC_NONNULL 169 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx) 170 { 171 int valid; 172 173 valid = pldm_msgbuf_validate(ctx); 174 175 ctx->cursor = NULL; 176 ctx->remaining = 0; 177 178 return valid; 179 } 180 181 /** 182 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer 183 * has been completely consumed without overflow 184 * 185 * @param[in] ctx - pldm_msgbuf context 186 * 187 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely 188 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx 189 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would 190 * have occurred byond the bounds of the buffer 191 */ 192 LIBPLDM_CC_NONNULL 193 LIBPLDM_CC_ALWAYS_INLINE int 194 pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx) 195 { 196 int consumed; 197 198 consumed = pldm_msgbuf_consumed(ctx); 199 200 ctx->cursor = NULL; 201 ctx->remaining = 0; 202 203 return consumed; 204 } 205 206 /* 207 * Exploit the pre-processor to perform type checking by macro substitution. 208 * 209 * A C type is defined by its alignment as well as its object 210 * size, and compilers have a hammer to enforce it in the form of 211 * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in 212 * the libpldm public API this presents a problem: Naively attempting to use the 213 * msgbuf APIs on a member of a packed struct would yield an error. 214 * 215 * The msgbuf APIs are implemented such that data is moved through unaligned 216 * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must 217 * make the object pointers take a trip through `void *` at its API boundary. 218 * That presents a bit too much of an opportunity to non-surgically remove your 219 * own foot, so here we set about doing something to mitigate that as well. 220 * 221 * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness 222 * only for the purpose of object sizes, disregarding alignment. We have a few 223 * constraints that cause some headaches: 224 * 225 * 1. We have to perform the type-check before a call through a C function, 226 * as the function must take the object pointer argument as `void *`. 227 * Essentially, this constrains us to doing something with macros. 228 * 229 * 2. While libpldm is a C library, its test suite is written in C++ to take 230 * advantage of gtest. 231 * 232 * 3. Ideally we'd do something with C's `static_assert()`, however 233 * `static_assert()` is defined as void, and as we're constrained to macros, 234 * using `static_assert()` would require a statement-expression 235 * 236 * 4. Currently the project is built with `-std=c17`. CPP statement-expressions 237 * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for 238 * the purpose of enabling statement-expressions in this one instance. 239 * 240 * 5. We can achieve a conditional build error using `pldm_require_obj_type()`, 241 * however it's implemented in terms of `_Generic()`, which is not available 242 * in C++. 243 * 244 * Combined this means we need separate solutions for C and C++. 245 * 246 * For C, as we don't have statement-expressions, we need to exploit some other 247 * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf 248 * API function call. We also have to take care of the fact that the call-sites 249 * may be in the context of a variable assignment for error-handling purposes. 250 * The key observation is that we can use the comma operator as a sequence point 251 * to order the type check before the API call, discarding the "result" value of 252 * the type check and yielding the return value of the API call. 253 * 254 * C++ could be less of a headache than the C as we can leverage template 255 * functions. An advantage of template functions is that while their definition 256 * is driven by instantion, the definition does not appear at the source 257 * location of the instantiation, which gives it a great leg-up over the problems 258 * we have in the C path. However, the use of the msgbuf APIs in the test suite 259 * still makes things somewhat tricky, as the call-sites in the test suite are 260 * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that 261 * takes both the object type and the required type as template arguments, and 262 * then define the object pointer parameter as `void *` for a call through to 263 * the appropriate msgbuf API. However, because the msgbuf API call-sites are 264 * encapsulated in gtest macros, use of commas in the template specification 265 * causes pre-processor confusion. In this way we're constrained to only one 266 * template argument per function. 267 * 268 * Implement the C++ path using template functions that take the destination 269 * object type as a template argument, while the name of the function symbols 270 * are derived from the required type. The manual implementations of these 271 * appear at the end of the header. The type safety is actually enforced 272 * by `static_assert()` this time, as we can use statements as we're not 273 * constrained to an expression in the templated function body. 274 * 275 * The invocations of pldm_msgbuf_extract_typecheck() typically result in 276 * double-evaluation of some arguments. We're not yet bothered by this for two 277 * reasons: 278 * 279 * 1. The nature of the current call-sites are such that there are no 280 * argument expressions that result in undesirable side-effects 281 * 282 * 2. It's an API internal to the libpldm implementation, and we can fix things 283 * whenever something crops up the violates the observation in 1. 284 */ 285 #ifdef __cplusplus 286 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \ 287 pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__) 288 #else 289 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \ 290 (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__)) 291 #endif 292 293 /** 294 * @brief pldm_msgbuf extractor for a uint8_t 295 * 296 * @param[in,out] ctx - pldm_msgbuf context for extractor 297 * @param[out] dst - destination of extracted value 298 * 299 * @return PLDM_SUCCESS if buffer accesses were in-bounds, 300 * PLDM_ERROR_INVALID_LENGTH otherwise. 301 * PLDM_ERROR_INVALID_DATA if input a invalid ctx 302 */ 303 #define pldm_msgbuf_extract_uint8(ctx, dst) \ 304 pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \ 305 dst, ctx, (void *)&(dst)) 306 LIBPLDM_CC_NONNULL 307 LIBPLDM_CC_ALWAYS_INLINE int 308 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 309 pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst) 310 { 311 if (!ctx->cursor) { 312 return -EINVAL; 313 } 314 315 if (ctx->remaining == INTMAX_MIN) { 316 assert(ctx->remaining < 0); 317 return -EOVERFLOW; 318 } 319 ctx->remaining -= sizeof(uint8_t); 320 assert(ctx->remaining >= 0); 321 if (ctx->remaining < 0) { 322 return -EOVERFLOW; 323 } 324 325 memcpy(dst, ctx->cursor, sizeof(uint8_t)); 326 327 ctx->cursor++; 328 return 0; 329 } 330 331 #define pldm_msgbuf_extract_int8(ctx, dst) \ 332 pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \ 333 ctx, (void *)&(dst)) 334 LIBPLDM_CC_NONNULL 335 LIBPLDM_CC_ALWAYS_INLINE int 336 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 337 pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst) 338 { 339 if (!ctx->cursor) { 340 return -EINVAL; 341 } 342 343 if (ctx->remaining == INTMAX_MIN) { 344 assert(ctx->remaining < 0); 345 return -EOVERFLOW; 346 } 347 ctx->remaining -= sizeof(int8_t); 348 assert(ctx->remaining >= 0); 349 if (ctx->remaining < 0) { 350 return -EOVERFLOW; 351 } 352 353 memcpy(dst, ctx->cursor, sizeof(int8_t)); 354 ctx->cursor++; 355 return 0; 356 } 357 358 #define pldm_msgbuf_extract_uint16(ctx, dst) \ 359 pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \ 360 dst, ctx, (void *)&(dst)) 361 LIBPLDM_CC_NONNULL 362 LIBPLDM_CC_ALWAYS_INLINE int 363 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 364 pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst) 365 { 366 uint16_t ldst; 367 368 if (!ctx->cursor) { 369 return -EINVAL; 370 } 371 372 // Check for underflow while tracking the magnitude of the buffer overflow 373 static_assert( 374 // NOLINTNEXTLINE(bugprone-sizeof-expression) 375 sizeof(ldst) < INTMAX_MAX, 376 "The following addition may not uphold the runtime assertion"); 377 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 378 assert(ctx->remaining < 0); 379 return -EOVERFLOW; 380 } 381 382 // Check for buffer overflow. If we overflow, account for the request as 383 // negative values in ctx->remaining. This way we can debug how far 384 // we've overflowed. 385 ctx->remaining -= sizeof(ldst); 386 387 // Prevent the access if it would overflow. First, assert so we blow up 388 // the test suite right at the point of failure. However, cater to 389 // -DNDEBUG by explicitly testing that the access is valid. 390 assert(ctx->remaining >= 0); 391 if (ctx->remaining < 0) { 392 return -EOVERFLOW; 393 } 394 395 // Use memcpy() to have the compiler deal with any alignment 396 // issues on the target architecture 397 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 398 399 // Only assign the target value once it's correctly decoded 400 ldst = le16toh(ldst); 401 402 // Allow storing to unaligned 403 memcpy(dst, &ldst, sizeof(ldst)); 404 405 ctx->cursor += sizeof(ldst); 406 407 return 0; 408 } 409 410 #define pldm_msgbuf_extract_int16(ctx, dst) \ 411 pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \ 412 dst, ctx, (void *)&(dst)) 413 LIBPLDM_CC_NONNULL 414 LIBPLDM_CC_ALWAYS_INLINE int 415 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 416 pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst) 417 { 418 int16_t ldst; 419 420 if (!ctx->cursor) { 421 return -EINVAL; 422 } 423 424 static_assert( 425 // NOLINTNEXTLINE(bugprone-sizeof-expression) 426 sizeof(ldst) < INTMAX_MAX, 427 "The following addition may not uphold the runtime assertion"); 428 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 429 assert(ctx->remaining < 0); 430 return -EOVERFLOW; 431 } 432 ctx->remaining -= sizeof(ldst); 433 assert(ctx->remaining >= 0); 434 if (ctx->remaining < 0) { 435 return -EOVERFLOW; 436 } 437 438 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 439 440 ldst = le16toh(ldst); 441 memcpy(dst, &ldst, sizeof(ldst)); 442 ctx->cursor += sizeof(ldst); 443 444 return 0; 445 } 446 447 #define pldm_msgbuf_extract_uint32(ctx, dst) \ 448 pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \ 449 dst, ctx, (void *)&(dst)) 450 LIBPLDM_CC_NONNULL 451 LIBPLDM_CC_ALWAYS_INLINE int 452 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 453 pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst) 454 { 455 uint32_t ldst; 456 457 if (!ctx->cursor) { 458 return -EINVAL; 459 } 460 461 static_assert( 462 // NOLINTNEXTLINE(bugprone-sizeof-expression) 463 sizeof(ldst) < INTMAX_MAX, 464 "The following addition may not uphold the runtime assertion"); 465 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 466 assert(ctx->remaining < 0); 467 return -EOVERFLOW; 468 } 469 ctx->remaining -= sizeof(ldst); 470 assert(ctx->remaining >= 0); 471 if (ctx->remaining < 0) { 472 return -EOVERFLOW; 473 } 474 475 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 476 ldst = le32toh(ldst); 477 memcpy(dst, &ldst, sizeof(ldst)); 478 ctx->cursor += sizeof(ldst); 479 480 return 0; 481 } 482 483 #define pldm_msgbuf_extract_int32(ctx, dst) \ 484 pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \ 485 dst, ctx, (void *)&(dst)) 486 LIBPLDM_CC_NONNULL 487 LIBPLDM_CC_ALWAYS_INLINE int 488 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 489 pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst) 490 { 491 int32_t ldst; 492 493 if (!ctx->cursor) { 494 return -EINVAL; 495 } 496 497 static_assert( 498 // NOLINTNEXTLINE(bugprone-sizeof-expression) 499 sizeof(ldst) < INTMAX_MAX, 500 "The following addition may not uphold the runtime assertion"); 501 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 502 assert(ctx->remaining < 0); 503 return -EOVERFLOW; 504 } 505 ctx->remaining -= sizeof(ldst); 506 assert(ctx->remaining >= 0); 507 if (ctx->remaining < 0) { 508 return -EOVERFLOW; 509 } 510 511 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 512 ldst = le32toh(ldst); 513 memcpy(dst, &ldst, sizeof(ldst)); 514 ctx->cursor += sizeof(ldst); 515 516 return PLDM_SUCCESS; 517 } 518 519 #define pldm_msgbuf_extract_real32(ctx, dst) \ 520 pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \ 521 dst, ctx, (void *)&(dst)) 522 LIBPLDM_CC_NONNULL 523 LIBPLDM_CC_ALWAYS_INLINE int 524 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 525 pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst) 526 { 527 uint32_t ldst; 528 529 static_assert(sizeof(real32_t) == sizeof(ldst), 530 "Mismatched type sizes for dst and ldst"); 531 532 if (!ctx->cursor) { 533 return -EINVAL; 534 } 535 536 static_assert( 537 // NOLINTNEXTLINE(bugprone-sizeof-expression) 538 sizeof(ldst) < INTMAX_MAX, 539 "The following addition may not uphold the runtime assertion"); 540 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 541 assert(ctx->remaining < 0); 542 return -EOVERFLOW; 543 } 544 ctx->remaining -= sizeof(ldst); 545 assert(ctx->remaining >= 0); 546 if (ctx->remaining < 0) { 547 return -EOVERFLOW; 548 } 549 550 memcpy(&ldst, ctx->cursor, sizeof(ldst)); 551 ldst = le32toh(ldst); 552 memcpy(dst, &ldst, sizeof(ldst)); 553 ctx->cursor += sizeof(ldst); 554 555 return 0; 556 } 557 558 /** 559 * Extract the field at the msgbuf cursor into the lvalue named by dst. 560 * 561 * @param ctx The msgbuf context object 562 * @param dst The lvalue into which the field at the msgbuf cursor should be 563 * extracted 564 * 565 * @return PLDM_SUCCESS on success, otherwise another value on error 566 */ 567 #define pldm_msgbuf_extract(ctx, dst) \ 568 _Generic((dst), \ 569 uint8_t: pldm__msgbuf_extract_uint8, \ 570 int8_t: pldm__msgbuf_extract_int8, \ 571 uint16_t: pldm__msgbuf_extract_uint16, \ 572 int16_t: pldm__msgbuf_extract_int16, \ 573 uint32_t: pldm__msgbuf_extract_uint32, \ 574 int32_t: pldm__msgbuf_extract_int32, \ 575 real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst)) 576 577 /** 578 * Extract the field at the msgbuf cursor into the object pointed-to by dst. 579 * 580 * @param ctx The msgbuf context object 581 * @param dst The pointer to the object into which the field at the msgbuf 582 * cursor should be extracted 583 * 584 * @return PLDM_SUCCESS on success, otherwise another value on error 585 */ 586 #define pldm_msgbuf_extract_p(ctx, dst) \ 587 _Generic((dst), \ 588 uint8_t *: pldm__msgbuf_extract_uint8, \ 589 int8_t *: pldm__msgbuf_extract_int8, \ 590 uint16_t *: pldm__msgbuf_extract_uint16, \ 591 int16_t *: pldm__msgbuf_extract_int16, \ 592 uint32_t *: pldm__msgbuf_extract_uint32, \ 593 int32_t *: pldm__msgbuf_extract_int32, \ 594 real32_t *: pldm__msgbuf_extract_real32)(ctx, dst) 595 596 /** 597 * @ref pldm_msgbuf_extract_array 598 */ 599 LIBPLDM_CC_NONNULL 600 LIBPLDM_CC_WARN_UNUSED_RESULT 601 LIBPLDM_CC_ALWAYS_INLINE int 602 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 603 pldm__msgbuf_extract_array_void(struct pldm_msgbuf *ctx, size_t count, 604 void *dst, size_t dst_count) 605 { 606 if (!ctx->cursor || count > dst_count) { 607 return -EINVAL; 608 } 609 610 if (!count) { 611 return 0; 612 } 613 614 #if INTMAX_MAX < SIZE_MAX 615 if (count > INTMAX_MAX) { 616 return -EOVERFLOW; 617 } 618 #endif 619 620 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) { 621 return -EOVERFLOW; 622 } 623 ctx->remaining -= (intmax_t)count; 624 assert(ctx->remaining >= 0); 625 if (ctx->remaining < 0) { 626 return -EOVERFLOW; 627 } 628 629 memcpy(dst, ctx->cursor, count); 630 ctx->cursor += count; 631 632 return 0; 633 } 634 635 /** 636 * @ref pldm_msgbuf_extract_array 637 */ 638 LIBPLDM_CC_NONNULL 639 LIBPLDM_CC_WARN_UNUSED_RESULT 640 LIBPLDM_CC_ALWAYS_INLINE int 641 pldm_msgbuf_extract_array_char(struct pldm_msgbuf *ctx, size_t count, char *dst, 642 size_t dst_count) 643 { 644 return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count); 645 } 646 647 /** 648 * @ref pldm_msgbuf_extract_array 649 */ 650 LIBPLDM_CC_NONNULL 651 LIBPLDM_CC_WARN_UNUSED_RESULT 652 LIBPLDM_CC_ALWAYS_INLINE int 653 pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, size_t count, 654 uint8_t *dst, size_t dst_count) 655 { 656 return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count); 657 } 658 659 /** 660 * Extract an array of data from the msgbuf instance 661 * 662 * @param ctx - The msgbuf instance from which to extract an array of data 663 * @param count - The number of array elements to extract 664 * @param dst - The array object into which elements from @p ctx should be 665 extracted 666 * @param dst_count - The maximum number of elements to place into @p dst 667 * 668 * Note that both @p count and @p dst_count can only be counted by `sizeof` for 669 * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number 670 * of array elements and _not_ the object size of the array. 671 */ 672 #define pldm_msgbuf_extract_array(ctx, count, dst, dst_count) \ 673 _Generic((*(dst)), \ 674 uint8_t: pldm_msgbuf_extract_array_uint8, \ 675 char: pldm_msgbuf_extract_array_char)(ctx, count, dst, \ 676 dst_count) 677 678 LIBPLDM_CC_NONNULL 679 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, 680 const uint32_t src) 681 { 682 uint32_t val = htole32(src); 683 684 if (!ctx->cursor) { 685 return -EINVAL; 686 } 687 688 static_assert( 689 // NOLINTNEXTLINE(bugprone-sizeof-expression) 690 sizeof(src) < INTMAX_MAX, 691 "The following addition may not uphold the runtime assertion"); 692 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 693 assert(ctx->remaining < 0); 694 return -EOVERFLOW; 695 } 696 ctx->remaining -= sizeof(src); 697 assert(ctx->remaining >= 0); 698 if (ctx->remaining < 0) { 699 return -EOVERFLOW; 700 } 701 702 memcpy(ctx->cursor, &val, sizeof(val)); 703 ctx->cursor += sizeof(src); 704 705 return 0; 706 } 707 708 LIBPLDM_CC_NONNULL 709 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, 710 const uint16_t src) 711 { 712 uint16_t val = htole16(src); 713 714 if (!ctx->cursor) { 715 return -EINVAL; 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 -EOVERFLOW; 725 } 726 ctx->remaining -= sizeof(src); 727 assert(ctx->remaining >= 0); 728 if (ctx->remaining < 0) { 729 return -EOVERFLOW; 730 } 731 732 memcpy(ctx->cursor, &val, sizeof(val)); 733 ctx->cursor += sizeof(src); 734 735 return 0; 736 } 737 738 LIBPLDM_CC_NONNULL 739 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, 740 const uint8_t src) 741 { 742 if (!ctx->cursor) { 743 return -EINVAL; 744 } 745 746 static_assert( 747 // NOLINTNEXTLINE(bugprone-sizeof-expression) 748 sizeof(src) < INTMAX_MAX, 749 "The following addition may not uphold the runtime assertion"); 750 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 751 assert(ctx->remaining < 0); 752 return -EOVERFLOW; 753 } 754 ctx->remaining -= sizeof(src); 755 assert(ctx->remaining >= 0); 756 if (ctx->remaining < 0) { 757 return -EOVERFLOW; 758 } 759 760 memcpy(ctx->cursor, &src, sizeof(src)); 761 ctx->cursor += sizeof(src); 762 763 return 0; 764 } 765 766 LIBPLDM_CC_NONNULL 767 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, 768 const int32_t src) 769 { 770 int32_t val = htole32(src); 771 772 if (!ctx->cursor) { 773 return -EINVAL; 774 } 775 776 static_assert( 777 // NOLINTNEXTLINE(bugprone-sizeof-expression) 778 sizeof(src) < INTMAX_MAX, 779 "The following addition may not uphold the runtime assertion"); 780 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 781 assert(ctx->remaining < 0); 782 return -EOVERFLOW; 783 } 784 ctx->remaining -= sizeof(src); 785 assert(ctx->remaining >= 0); 786 if (ctx->remaining < 0) { 787 return -EOVERFLOW; 788 } 789 790 memcpy(ctx->cursor, &val, sizeof(val)); 791 ctx->cursor += sizeof(src); 792 793 return 0; 794 } 795 796 LIBPLDM_CC_NONNULL 797 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, 798 const int16_t src) 799 { 800 int16_t val = htole16(src); 801 802 if (!ctx->cursor) { 803 return -EINVAL; 804 } 805 806 static_assert( 807 // NOLINTNEXTLINE(bugprone-sizeof-expression) 808 sizeof(src) < INTMAX_MAX, 809 "The following addition may not uphold the runtime assertion"); 810 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 811 assert(ctx->remaining < 0); 812 return -EOVERFLOW; 813 } 814 ctx->remaining -= sizeof(src); 815 assert(ctx->remaining >= 0); 816 if (ctx->remaining < 0) { 817 return -EOVERFLOW; 818 } 819 820 memcpy(ctx->cursor, &val, sizeof(val)); 821 ctx->cursor += sizeof(src); 822 823 return 0; 824 } 825 826 LIBPLDM_CC_NONNULL 827 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, 828 const int8_t src) 829 { 830 if (!ctx->cursor) { 831 return -EINVAL; 832 } 833 834 static_assert( 835 // NOLINTNEXTLINE(bugprone-sizeof-expression) 836 sizeof(src) < INTMAX_MAX, 837 "The following addition may not uphold the runtime assertion"); 838 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 839 assert(ctx->remaining < 0); 840 return -EOVERFLOW; 841 } 842 ctx->remaining -= sizeof(src); 843 assert(ctx->remaining >= 0); 844 if (ctx->remaining < 0) { 845 return -EOVERFLOW; 846 } 847 848 memcpy(ctx->cursor, &src, sizeof(src)); 849 ctx->cursor += sizeof(src); 850 851 return 0; 852 } 853 854 #define pldm_msgbuf_insert(dst, src) \ 855 _Generic((src), \ 856 uint8_t: pldm_msgbuf_insert_uint8, \ 857 int8_t: pldm_msgbuf_insert_int8, \ 858 uint16_t: pldm_msgbuf_insert_uint16, \ 859 int16_t: pldm_msgbuf_insert_int16, \ 860 uint32_t: pldm_msgbuf_insert_uint32, \ 861 int32_t: pldm_msgbuf_insert_int32)(dst, src) 862 863 /** 864 * @ref pldm_msgbuf_insert_array 865 */ 866 LIBPLDM_CC_NONNULL 867 LIBPLDM_CC_WARN_UNUSED_RESULT 868 LIBPLDM_CC_ALWAYS_INLINE int 869 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 870 pldm__msgbuf_insert_array_void(struct pldm_msgbuf *ctx, size_t count, 871 const void *src, size_t src_count) 872 { 873 if (!ctx->cursor || count > src_count) { 874 return -EINVAL; 875 } 876 877 if (!count) { 878 return 0; 879 } 880 881 #if INTMAX_MAX < SIZE_MAX 882 if (count > INTMAX_MAX) { 883 return -EOVERFLOW; 884 } 885 #endif 886 887 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) { 888 return -EOVERFLOW; 889 } 890 ctx->remaining -= (intmax_t)count; 891 assert(ctx->remaining >= 0); 892 if (ctx->remaining < 0) { 893 return -EOVERFLOW; 894 } 895 896 memcpy(ctx->cursor, src, count); 897 ctx->cursor += count; 898 899 return 0; 900 } 901 902 /** 903 * @ref pldm_msgbuf_insert_array 904 */ 905 LIBPLDM_CC_NONNULL 906 LIBPLDM_CC_WARN_UNUSED_RESULT 907 LIBPLDM_CC_ALWAYS_INLINE int 908 pldm_msgbuf_insert_array_char(struct pldm_msgbuf *ctx, size_t count, 909 const char *src, size_t src_count) 910 { 911 return pldm__msgbuf_insert_array_void(ctx, count, src, src_count); 912 } 913 914 /** 915 * @ref pldm_msgbuf_insert_array 916 */ 917 LIBPLDM_CC_NONNULL 918 LIBPLDM_CC_WARN_UNUSED_RESULT 919 LIBPLDM_CC_ALWAYS_INLINE int 920 pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, size_t count, 921 const uint8_t *src, size_t src_count) 922 { 923 return pldm__msgbuf_insert_array_void(ctx, count, src, src_count); 924 } 925 926 /** 927 * Insert an array of data into the msgbuf instance 928 * 929 * @param ctx - The msgbuf instance into which the array of data should be 930 * inserted 931 * @param count - The number of array elements to insert 932 * @param src - The array object from which elements should be inserted into 933 @p ctx 934 * @param src_count - The maximum number of elements to insert from @p src 935 * 936 * Note that both @p count and @p src_count can only be counted by `sizeof` for 937 * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number 938 * of array elements and _not_ the object size of the array. 939 */ 940 #define pldm_msgbuf_insert_array(dst, count, src, src_count) \ 941 _Generic((*(src)), \ 942 uint8_t: pldm_msgbuf_insert_array_uint8, \ 943 char: pldm_msgbuf_insert_array_char)(dst, count, src, \ 944 src_count) 945 946 LIBPLDM_CC_NONNULL_ARGS(1) 947 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, 948 size_t required, 949 void **cursor) 950 { 951 if (!ctx->cursor || (cursor && *cursor)) { 952 return -EINVAL; 953 } 954 955 #if INTMAX_MAX < SIZE_MAX 956 if (required > INTMAX_MAX) { 957 return -EOVERFLOW; 958 } 959 #endif 960 961 if (ctx->remaining < INTMAX_MIN + (intmax_t)required) { 962 return -EOVERFLOW; 963 } 964 ctx->remaining -= (intmax_t)required; 965 assert(ctx->remaining >= 0); 966 if (ctx->remaining < 0) { 967 return -EOVERFLOW; 968 } 969 970 if (cursor) { 971 *cursor = ctx->cursor; 972 } 973 ctx->cursor += required; 974 975 return 0; 976 } 977 978 LIBPLDM_CC_NONNULL_ARGS(1) 979 LIBPLDM_CC_ALWAYS_INLINE int 980 pldm_msgbuf_span_string_ascii(struct pldm_msgbuf *ctx, void **cursor, 981 size_t *length) 982 { 983 intmax_t measured; 984 985 if (!ctx->cursor || (cursor && *cursor)) { 986 return -EINVAL; 987 } 988 989 if (ctx->remaining < 0) { 990 /* Tracking the amount of overflow gets disturbed here */ 991 return -EOVERFLOW; 992 } 993 994 measured = (intmax_t)strnlen((const char *)ctx->cursor, ctx->remaining); 995 if (measured == ctx->remaining) { 996 /* 997 * We have hit the end of the buffer prior to the NUL terminator. 998 * Optimistically, the NUL terminator was one-beyond-the-end. Setting 999 * ctx->remaining negative ensures the `pldm_msgbuf_destroy*()` APIs also 1000 * return an error. 1001 */ 1002 ctx->remaining = -1; 1003 return -EOVERFLOW; 1004 } 1005 1006 /* Include the NUL terminator in the span length, as spans are opaque */ 1007 measured++; 1008 1009 if (ctx->remaining < INTMAX_MIN + measured) { 1010 return -EOVERFLOW; 1011 } 1012 1013 ctx->remaining -= measured; 1014 assert(ctx->remaining >= 0); 1015 if (ctx->remaining < 0) { 1016 return -EOVERFLOW; 1017 } 1018 1019 if (cursor) { 1020 *cursor = ctx->cursor; 1021 } 1022 1023 ctx->cursor += measured; 1024 1025 if (length) { 1026 *length = measured; 1027 } 1028 1029 return 0; 1030 } 1031 1032 LIBPLDM_CC_NONNULL_ARGS(1) 1033 LIBPLDM_CC_ALWAYS_INLINE int 1034 pldm_msgbuf_span_string_utf16(struct pldm_msgbuf *ctx, void **cursor, 1035 size_t *length) 1036 { 1037 static const char16_t term = 0; 1038 ptrdiff_t measured; 1039 void *end; 1040 1041 if (!ctx->cursor || (cursor && *cursor)) { 1042 return -EINVAL; 1043 } 1044 1045 if (ctx->remaining < 0) { 1046 /* Tracking the amount of overflow gets disturbed here */ 1047 return -EOVERFLOW; 1048 } 1049 1050 /* 1051 * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do 1052 * not form a UTF16 NUL _code-point_ due to alignment with respect to the 1053 * start of the string 1054 */ 1055 end = ctx->cursor; 1056 do { 1057 if (end != ctx->cursor) { 1058 /* 1059 * If we've looped we've found a relatively-unaligned NUL code-point. 1060 * Scan again from a relatively-aligned start point. 1061 */ 1062 end = (char *)end + 1; 1063 } 1064 measured = (char *)end - (char *)ctx->cursor; 1065 end = memmem(end, ctx->remaining - measured, &term, 1066 sizeof(term)); 1067 } while (end && ((uintptr_t)end & 1) != ((uintptr_t)ctx->cursor & 1)); 1068 1069 if (!end) { 1070 /* 1071 * Optimistically, the last required pattern byte was one beyond the end of 1072 * the buffer. Setting ctx->remaining negative ensures the 1073 * `pldm_msgbuf_destroy*()` APIs also return an error. 1074 */ 1075 ctx->remaining = -1; 1076 return -EOVERFLOW; 1077 } 1078 1079 end = (char *)end + sizeof(char16_t); 1080 measured = (char *)end - (char *)ctx->cursor; 1081 1082 #if INTMAX_MAX < PTRDIFF_MAX 1083 if (measured >= INTMAX_MAX) { 1084 return pldm_msgbuf_status(ctx, EOVERFLOW); 1085 } 1086 #endif 1087 1088 if (ctx->remaining < INTMAX_MIN + (intmax_t)measured) { 1089 assert(ctx->remaining < 0); 1090 return -EOVERFLOW; 1091 } 1092 1093 ctx->remaining -= (intmax_t)measured; 1094 assert(ctx->remaining >= 0); 1095 if (ctx->remaining < 0) { 1096 return -EOVERFLOW; 1097 } 1098 1099 if (cursor) { 1100 *cursor = ctx->cursor; 1101 } 1102 1103 ctx->cursor += measured; 1104 1105 if (length) { 1106 *length = (size_t)measured; 1107 } 1108 1109 return 0; 1110 } 1111 1112 LIBPLDM_CC_NONNULL 1113 LIBPLDM_CC_ALWAYS_INLINE int 1114 pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len) 1115 { 1116 if (!ctx->cursor || *cursor) { 1117 return -EINVAL; 1118 } 1119 1120 assert(ctx->remaining >= 0); 1121 if (ctx->remaining < 0) { 1122 return -EOVERFLOW; 1123 } 1124 1125 *cursor = ctx->cursor; 1126 ctx->cursor += ctx->remaining; 1127 *len = ctx->remaining; 1128 ctx->remaining = 0; 1129 1130 return 0; 1131 } 1132 1133 /** 1134 * @brief pldm_msgbuf copy data between two msg buffers 1135 * 1136 * @param[in,out] src - pldm_msgbuf for source from where value should be copied 1137 * @param[in,out] dst - destination of copy from source 1138 * @param[in] size - size of data to be copied 1139 * @param[in] description - description of data copied 1140 * 1141 * @return PLDM_SUCCESS if buffer accesses were in-bounds, 1142 * PLDM_ERROR_INVALID_LENGTH otherwise. 1143 * PLDM_ERROR_INVALID_DATA if input is invalid 1144 */ 1145 #define pldm_msgbuf_copy(dst, src, type, name) \ 1146 pldm__msgbuf_copy(dst, src, sizeof(type), #name) 1147 LIBPLDM_CC_NONNULL 1148 LIBPLDM_CC_ALWAYS_INLINE int 1149 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 1150 pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size, 1151 const char *description LIBPLDM_CC_UNUSED) 1152 { 1153 if (!src->cursor || !dst->cursor) { 1154 return -EINVAL; 1155 } 1156 1157 #if INTMAX_MAX < SIZE_MAX 1158 if (size > INTMAX_MAX) { 1159 return -EOVERFLOW; 1160 } 1161 #endif 1162 1163 if (src->remaining < INTMAX_MIN + (intmax_t)size) { 1164 return -EOVERFLOW; 1165 } 1166 1167 if (dst->remaining < INTMAX_MIN + (intmax_t)size) { 1168 return -EOVERFLOW; 1169 } 1170 1171 src->remaining -= (intmax_t)size; 1172 assert(src->remaining >= 0); 1173 if (src->remaining < 0) { 1174 return -EOVERFLOW; 1175 } 1176 1177 dst->remaining -= (intmax_t)size; 1178 assert(dst->remaining >= 0); 1179 if (dst->remaining < 0) { 1180 return -EOVERFLOW; 1181 } 1182 1183 memcpy(dst->cursor, src->cursor, size); 1184 src->cursor += size; 1185 dst->cursor += size; 1186 1187 return 0; 1188 } 1189 1190 LIBPLDM_CC_NONNULL 1191 LIBPLDM_CC_WARN_UNUSED_RESULT 1192 LIBPLDM_CC_ALWAYS_INLINE int 1193 pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src) 1194 { 1195 void *ascii = NULL; 1196 size_t len = 0; 1197 int rc; 1198 1199 rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len); 1200 if (rc < 0) { 1201 return rc; 1202 } 1203 1204 return pldm__msgbuf_insert_array_void(dst, len, ascii, len); 1205 } 1206 1207 LIBPLDM_CC_NONNULL 1208 LIBPLDM_CC_WARN_UNUSED_RESULT 1209 LIBPLDM_CC_ALWAYS_INLINE int 1210 pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src) 1211 { 1212 void *utf16 = NULL; 1213 size_t len = 0; 1214 int rc; 1215 1216 rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len); 1217 if (rc < 0) { 1218 return rc; 1219 } 1220 1221 return pldm__msgbuf_insert_array_void(dst, len, utf16, len); 1222 } 1223 1224 #ifdef __cplusplus 1225 } 1226 #endif 1227 1228 #ifdef __cplusplus 1229 #include <type_traits> 1230 1231 template <typename T> 1232 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx, 1233 void *buf) 1234 { 1235 static_assert(std::is_same<uint8_t, T>::value); 1236 return pldm__msgbuf_extract_uint8(ctx, buf); 1237 } 1238 1239 template <typename T> 1240 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx, 1241 void *buf) 1242 { 1243 static_assert(std::is_same<int8_t, T>::value); 1244 return pldm__msgbuf_extract_int8(ctx, buf); 1245 } 1246 1247 template <typename T> 1248 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx, 1249 void *buf) 1250 { 1251 static_assert(std::is_same<uint16_t, T>::value); 1252 return pldm__msgbuf_extract_uint16(ctx, buf); 1253 } 1254 1255 template <typename T> 1256 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx, 1257 void *buf) 1258 { 1259 static_assert(std::is_same<int16_t, T>::value); 1260 return pldm__msgbuf_extract_int16(ctx, buf); 1261 } 1262 1263 template <typename T> 1264 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx, 1265 void *buf) 1266 { 1267 static_assert(std::is_same<uint32_t, T>::value); 1268 return pldm__msgbuf_extract_uint32(ctx, buf); 1269 } 1270 1271 template <typename T> 1272 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx, 1273 void *buf) 1274 { 1275 static_assert(std::is_same<int32_t, T>::value); 1276 return pldm__msgbuf_extract_int32(ctx, buf); 1277 } 1278 1279 template <typename T> 1280 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx, 1281 void *buf) 1282 { 1283 static_assert(std::is_same<real32_t, T>::value); 1284 return pldm__msgbuf_extract_real32(ctx, buf); 1285 } 1286 #endif 1287 1288 #endif /* BUF_H */ 1289