/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ #ifndef PLDM_MSGBUF_CORE_H #define PLDM_MSGBUF_CORE_H /* * Historically, many of the structs exposed in libpldm's public headers are * defined with __attribute__((packed)). This is unfortunate: it gives the * impression that a wire-format buffer can be cast to the message type to make * the message's fields easily accessible. As it turns out, that's not * that's valid for several reasons: * * 1. Casting the wire-format buffer to a struct of the message type doesn't * abstract the endianness of message field values * * 2. Some messages contain packed tagged union fields which cannot be properly * described in a C struct. * * The msgbuf APIs exist to assist with (un)packing the wire-format in a way * that is type-safe, spatially memory-safe, endian-safe, performant, and * free of undefined-behaviour. Message structs that are added to the public * library API should no-longer be marked __attribute__((packed)), and the * implementation of their encode and decode functions must exploit the msgbuf * API. * * However, we would like to allow implementation of codec functions in terms of * msgbuf APIs even if they're decoding a message into a (historically) packed * struct. Some of the complexity that follows is a consequence of the packed/ * unpacked conflict. */ #ifdef __cplusplus /* * Fix up C11's _Static_assert() vs C++'s static_assert(). * * Can we please have nice things for once. */ // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) #define _Static_assert(...) static_assert(__VA_ARGS__) extern "C" { #endif #include "compiler.h" #include #include #include #include #include #include #include /* * We can't use static_assert() outside of some other C construct. Deal * with high-level global assertions by burying them in an unused struct * declaration, that has a sole member for compliance with the requirement that * types must have a size. */ static struct { static_assert( INTMAX_MAX != SIZE_MAX, "Extraction and insertion value comparisons may be broken"); static_assert(INTMAX_MIN + INTMAX_MAX <= 0, "Extraction and insertion arithmetic may be broken"); int compliance; } build_assertions LIBPLDM_CC_UNUSED; struct pldm_msgbuf_rw { uint8_t *cursor; intmax_t remaining; }; struct pldm_msgbuf_ro { const uint8_t *cursor; intmax_t remaining; }; LIBPLDM_CC_ALWAYS_INLINE // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) void pldm__msgbuf_cleanup(const void *cursor LIBPLDM_CC_UNUSED, intmax_t remaining LIBPLDM_CC_UNUSED) { assert(cursor == NULL && remaining == INTMAX_MIN); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) void pldm__msgbuf_rw_cleanup(struct pldm_msgbuf_rw *ctx LIBPLDM_CC_UNUSED) { pldm__msgbuf_cleanup((const void *)ctx->cursor, ctx->remaining); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) void pldm__msgbuf_ro_cleanup(struct pldm_msgbuf_ro *ctx LIBPLDM_CC_UNUSED) { pldm__msgbuf_cleanup((const void *)ctx->cursor, ctx->remaining); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) int pldm__msgbuf_set_invalid(intmax_t *remaining) { *remaining = INTMAX_MIN; return -EOVERFLOW; } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) int pldm__msgbuf_rw_invalidate(struct pldm_msgbuf_rw *ctx) { return pldm__msgbuf_set_invalid(&ctx->remaining); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) int pldm__msgbuf_ro_invalidate(struct pldm_msgbuf_ro *ctx) { return pldm__msgbuf_set_invalid(&ctx->remaining); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_init_errno(const uint8_t **cursor, intmax_t *remaining, size_t minsize, const void *buf, size_t len) { *cursor = NULL; if ((minsize > len)) { return pldm__msgbuf_set_invalid(remaining); } #if INTMAX_MAX < SIZE_MAX if (len > INTMAX_MAX) { return pldm__msgbuf_set_invalid(remaining); } #endif if (UINTPTR_MAX - (uintptr_t)buf < len) { return pldm__msgbuf_set_invalid(remaining); } *cursor = (const uint8_t *)buf; *remaining = (intmax_t)len; return 0; } /** * @brief Initialize pldm buf struct for buf extractor * * @param[out] ctx - pldm_msgbuf_rw context for extractor * @param[in] minsize - The minimum required length of buffer `buf` * @param[in] buf - buffer to be extracted * @param[in] len - size of buffer * * @return 0 on success, otherwise an error code appropriate for the current * personality. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm_msgbuf_rw_init_errno(struct pldm_msgbuf_rw *ctx, size_t minsize, const void *buf, size_t len) { return pldm__msgbuf_init_errno((const uint8_t **)&ctx->cursor, &ctx->remaining, minsize, buf, len); } /** * @brief Initialize pldm buf struct for buf extractor * * @param[out] ctx - pldm_msgbuf_ro context for extractor * @param[in] minsize - The minimum required length of buffer `buf` * @param[in] buf - buffer to be extracted * @param[in] len - size of buffer * * @return 0 on success, otherwise an error code appropriate for the current * personality. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm_msgbuf_ro_init_errno(struct pldm_msgbuf_ro *ctx, size_t minsize, const void *buf, size_t len) { return pldm__msgbuf_init_errno(&ctx->cursor, &ctx->remaining, minsize, buf, len); } /** * @brief Validate buffer overflow state * * @param[in] ctx - msgbuf context for extractor * * @return PLDM_SUCCESS if there are zero or more bytes of data that remain * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a * prior accesses would have occurred beyond the bounds of the buffer, and * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid * pointer. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) int pldm__msgbuf_validate(intmax_t remaining) { if (remaining < 0) { return -EOVERFLOW; } return 0; } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_ro_validate(struct pldm_msgbuf_ro *ctx) { return pldm__msgbuf_validate(ctx->remaining); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_rw_validate(struct pldm_msgbuf_rw *ctx) { return pldm__msgbuf_validate(ctx->remaining); } /** * @brief Test whether a message buffer has been exactly consumed * * @param[in] ctx - pldm_msgbuf context for extractor * * @return 0 iff there are zero bytes of data that remain unread from the buffer * and no overflow has occurred. Otherwise, -EBADMSG if the buffer has not been * completely consumed, or -EOVERFLOW if accesses were attempted beyond the * bounds of the buffer. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) int pldm__msgbuf_consumed(intmax_t remaining) { if (remaining > 0) { return -EBADMSG; } if (remaining < 0) { return -EOVERFLOW; } return 0; } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_ro_consumed(struct pldm_msgbuf_ro *ctx) { return pldm__msgbuf_consumed(ctx->remaining); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_rw_consumed(struct pldm_msgbuf_rw *ctx) { return pldm__msgbuf_consumed(ctx->remaining); } /** * @brief End use of a msgbuf under error conditions * * @param[in] ctx - The msgbuf instance to discard * @param[in] error - The error value to propagate * * Under normal conditions use of a msgbuf instance must be ended using @ref * pldm_msgbuf_complete or one of its related APIs. Under error conditions, @ref * pldm_msgbuf_discard should be used instead, as it makes it straight-forward * to finalise the msgbuf while propagating the existing error code. * * @return The value provided in @param error */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) int pldm__msgbuf_discard(const uint8_t **cursor, intmax_t *remaining, int error) { *cursor = NULL; pldm__msgbuf_set_invalid(remaining); return error; } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_rw_discard(struct pldm_msgbuf_rw *ctx, int error) { return pldm__msgbuf_discard((const uint8_t **)&ctx->cursor, &ctx->remaining, error); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_ro_discard(struct pldm_msgbuf_ro *ctx, int error) { return pldm__msgbuf_discard(&ctx->cursor, &ctx->remaining, error); } /** * @brief Complete the pldm_msgbuf_rw instance * * @param[in] ctx - pldm_msgbuf_rw context for extractor * * @return 0 if all buffer accesses were in-bounds, -EOVERFLOW otherwise. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_rw_complete(struct pldm_msgbuf_rw *ctx) { return pldm_msgbuf_rw_discard(ctx, pldm_msgbuf_rw_validate(ctx)); } /** * @brief Complete the pldm_msgbuf_ro instance * * @param[in] ctx - pldm_msgbuf_ro context for extractor * * @return 0 if all buffer accesses were in-bounds, -EOVERFLOW otherwise. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_ro_complete(struct pldm_msgbuf_ro *ctx) { return pldm_msgbuf_ro_discard(ctx, pldm_msgbuf_ro_validate(ctx)); } /** * @brief Complete the pldm_msgbuf_rw instance, and check that the underlying buffer * has been entirely consumed without overflow * * @param[in] ctx - pldm_msgbuf_rw context * * @return 0 if all buffer access were in-bounds and completely consume the * underlying buffer. Otherwise, -EBADMSG if the buffer has not been completely * consumed, or -EOVERFLOW if accesses were attempted beyond the bounds of the * buffer. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_rw_complete_consumed(struct pldm_msgbuf_rw *ctx) { return pldm_msgbuf_rw_discard(ctx, pldm_msgbuf_rw_consumed(ctx)); } /** * @brief Complete the pldm_msgbuf_ro instance, and check that the underlying buffer * has been entirely consumed without overflow * * @param[in] ctx - pldm_msgbuf_ro context * * @return 0 if all buffer access were in-bounds and completely consume the * underlying buffer. Otherwise, -EBADMSG if the buffer has not been completely * consumed, or -EOVERFLOW if accesses were attempted beyond the bounds of the * buffer. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_ro_complete_consumed(struct pldm_msgbuf_ro *ctx) { return pldm_msgbuf_ro_discard(ctx, pldm_msgbuf_ro_consumed(ctx)); } /* * Exploit the pre-processor to perform type checking by macro substitution. * * A C type is defined by its alignment as well as its object * size, and compilers have a hammer to enforce it in the form of * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in * the libpldm public API this presents a problem: Naively attempting to use the * msgbuf APIs on a member of a packed struct would yield an error. * * The msgbuf APIs are implemented such that data is moved through unaligned * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must * make the object pointers take a trip through `void *` at its API boundary. * That presents a bit too much of an opportunity to non-surgically remove your * own foot, so here we set about doing something to mitigate that as well. * * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness * only for the purpose of object sizes, disregarding alignment. We have a few * constraints that cause some headaches: * * 1. We have to perform the type-check before a call through a C function, * as the function must take the object pointer argument as `void *`. * Essentially, this constrains us to doing something with macros. * * 2. While libpldm is a C library, its test suite is written in C++ to take * advantage of gtest. * * 3. Ideally we'd do something with C's `static_assert()`, however * `static_assert()` is defined as void, and as we're constrained to macros, * using `static_assert()` would require a statement-expression * * 4. Currently the project is built with `-std=c17`. CPP statement-expressions * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for * the purpose of enabling statement-expressions in this one instance. * * 5. We can achieve a conditional build error using `pldm_require_obj_type()`, * however it's implemented in terms of `_Generic()`, which is not available * in C++. * * Combined this means we need separate solutions for C and C++. * * For C, as we don't have statement-expressions, we need to exploit some other * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf * API function call. We also have to take care of the fact that the call-sites * may be in the context of a variable assignment for error-handling purposes. * The key observation is that we can use the comma operator as a sequence point * to order the type check before the API call, discarding the "result" value of * the type check and yielding the return value of the API call. * * C++ could be less of a headache than the C as we can leverage template * functions. An advantage of template functions is that while their definition * is driven by instantion, the definition does not appear at the source * location of the instantiation, which gives it a great leg-up over the problems * we have in the C path. However, the use of the msgbuf APIs in the test suite * still makes things somewhat tricky, as the call-sites in the test suite are * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that * takes both the object type and the required type as template arguments, and * then define the object pointer parameter as `void *` for a call through to * the appropriate msgbuf API. However, because the msgbuf API call-sites are * encapsulated in gtest macros, use of commas in the template specification * causes pre-processor confusion. In this way we're constrained to only one * template argument per function. * * Implement the C++ path using template functions that take the destination * object type as a template argument, while the name of the function symbols * are derived from the required type. The manual implementations of these * appear at the end of the header. The type safety is actually enforced * by `static_assert()` this time, as we can use statements as we're not * constrained to an expression in the templated function body. * * The invocations of pldm_msgbuf_extract_typecheck() typically result in * double-evaluation of some arguments. We're not yet bothered by this for two * reasons: * * 1. The nature of the current call-sites are such that there are no * argument expressions that result in undesirable side-effects * * 2. It's an API internal to the libpldm implementation, and we can fix things * whenever something crops up the violates the observation in 1. */ /** * @brief pldm_msgbuf extractor for a uint8_t * * @param[in,out] ctx - pldm_msgbuf context for extractor * @param[out] dst - destination of extracted value * * @return PLDM_SUCCESS if buffer accesses were in-bounds, * PLDM_ERROR_INVALID_LENGTH otherwise. * PLDM_ERROR_INVALID_DATA if input a invalid ctx */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_uint8(struct pldm_msgbuf_ro *ctx, void *dst) { if (ctx->remaining >= (intmax_t)sizeof(uint8_t)) { assert(ctx->cursor); memcpy(dst, ctx->cursor, sizeof(uint8_t)); ctx->cursor++; ctx->remaining -= sizeof(uint8_t); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(uint8_t)) { ctx->remaining -= sizeof(uint8_t); return -EOVERFLOW; } return pldm__msgbuf_ro_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_int8(struct pldm_msgbuf_ro *ctx, void *dst) { if (ctx->remaining >= (intmax_t)sizeof(int8_t)) { assert(ctx->cursor); memcpy(dst, ctx->cursor, sizeof(int8_t)); ctx->cursor++; ctx->remaining -= sizeof(int8_t); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(int8_t)) { ctx->remaining -= sizeof(int8_t); return -EOVERFLOW; } return pldm__msgbuf_ro_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_uint16(struct pldm_msgbuf_ro *ctx, void *dst) { uint16_t ldst; // Check for underflow while tracking the magnitude of the buffer overflow static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(ldst) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(ldst)) { assert(ctx->cursor); // Use memcpy() to have the compiler deal with any alignment // issues on the target architecture memcpy(&ldst, ctx->cursor, sizeof(ldst)); // Only assign the target value once it's correctly decoded ldst = le16toh(ldst); // Allow storing to unaligned memcpy(dst, &ldst, sizeof(ldst)); ctx->cursor += sizeof(ldst); ctx->remaining -= sizeof(ldst); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { ctx->remaining -= sizeof(ldst); return -EOVERFLOW; } return pldm__msgbuf_ro_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_int16(struct pldm_msgbuf_ro *ctx, void *dst) { int16_t ldst; static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(ldst) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(ldst)) { assert(ctx->cursor); memcpy(&ldst, ctx->cursor, sizeof(ldst)); ldst = le16toh(ldst); memcpy(dst, &ldst, sizeof(ldst)); ctx->cursor += sizeof(ldst); ctx->remaining -= sizeof(ldst); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { ctx->remaining -= sizeof(ldst); return -EOVERFLOW; } return pldm__msgbuf_ro_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_uint32(struct pldm_msgbuf_ro *ctx, void *dst) { uint32_t ldst; static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(ldst) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(ldst)) { assert(ctx->cursor); memcpy(&ldst, ctx->cursor, sizeof(ldst)); ldst = le32toh(ldst); memcpy(dst, &ldst, sizeof(ldst)); ctx->cursor += sizeof(ldst); ctx->remaining -= sizeof(ldst); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { ctx->remaining -= sizeof(ldst); return -EOVERFLOW; } return pldm__msgbuf_ro_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_int32(struct pldm_msgbuf_ro *ctx, void *dst) { int32_t ldst; static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(ldst) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(ldst)) { assert(ctx->cursor); memcpy(&ldst, ctx->cursor, sizeof(ldst)); ldst = le32toh(ldst); memcpy(dst, &ldst, sizeof(ldst)); ctx->cursor += sizeof(ldst); ctx->remaining -= sizeof(ldst); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { ctx->remaining -= sizeof(ldst); return -EOVERFLOW; } return pldm__msgbuf_ro_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_real32(struct pldm_msgbuf_ro *ctx, void *dst) { uint32_t ldst; static_assert(sizeof(real32_t) == sizeof(ldst), "Mismatched type sizes for dst and ldst"); static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(ldst) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(ldst)) { assert(ctx->cursor); memcpy(&ldst, ctx->cursor, sizeof(ldst)); ldst = le32toh(ldst); memcpy(dst, &ldst, sizeof(ldst)); ctx->cursor += sizeof(ldst); ctx->remaining -= sizeof(ldst); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(ldst)) { ctx->remaining -= sizeof(ldst); return -EOVERFLOW; } return pldm__msgbuf_ro_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_WARN_UNUSED_RESULT LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_array_void(struct pldm_msgbuf_ro *ctx, size_t count, void *dst, size_t dst_count) { if (count > dst_count) { return -EINVAL; } if (!count) { return 0; } #if INTMAX_MAX < SIZE_MAX if (count > INTMAX_MAX) { return pldm__msgbuf_ro_invalidate(ctx); } #endif if (ctx->remaining >= (intmax_t)count) { assert(ctx->cursor); memcpy(dst, ctx->cursor, count); ctx->cursor += count; ctx->remaining -= (intmax_t)count; return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)count) { ctx->remaining -= (intmax_t)count; return -EOVERFLOW; } return pldm__msgbuf_ro_invalidate(ctx); } /** * @ref pldm_msgbuf_extract_array */ LIBPLDM_CC_NONNULL LIBPLDM_CC_WARN_UNUSED_RESULT LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_extract_array_char(struct pldm_msgbuf_ro *ctx, size_t count, char *dst, size_t dst_count) { return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count); } /** * @ref pldm_msgbuf_extract_array */ LIBPLDM_CC_NONNULL LIBPLDM_CC_WARN_UNUSED_RESULT LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf_ro *ctx, size_t count, uint8_t *dst, size_t dst_count) { return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count); } /** * Extract an array of data from the msgbuf instance * * @param ctx - The msgbuf instance from which to extract an array of data * @param count - The number of array elements to extract * @param dst - The array object into which elements from @p ctx should be extracted * @param dst_count - The maximum number of elements to place into @p dst * * Note that both @p count and @p dst_count can only be counted by `sizeof` for * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number * of array elements and _not_ the object size of the array. */ #define pldm_msgbuf_extract_array(ctx, count, dst, dst_count) \ _Generic((*(dst)), \ uint8_t: pldm_msgbuf_extract_array_uint8, \ char: pldm_msgbuf_extract_array_char)(ctx, count, dst, \ dst_count) LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint64(struct pldm_msgbuf_rw *ctx, const uint64_t src) { uint64_t val = htole64(src); static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(src) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(src)) { assert(ctx->cursor); memcpy(ctx->cursor, &val, sizeof(val)); ctx->cursor += sizeof(src); ctx->remaining -= sizeof(src); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { ctx->remaining -= sizeof(src); return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint32(struct pldm_msgbuf_rw *ctx, const uint32_t src) { uint32_t val = htole32(src); static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(src) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(src)) { assert(ctx->cursor); memcpy(ctx->cursor, &val, sizeof(val)); ctx->cursor += sizeof(src); ctx->remaining -= sizeof(src); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { ctx->remaining -= sizeof(src); return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint16(struct pldm_msgbuf_rw *ctx, const uint16_t src) { uint16_t val = htole16(src); static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(src) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(src)) { assert(ctx->cursor); memcpy(ctx->cursor, &val, sizeof(val)); ctx->cursor += sizeof(src); ctx->remaining -= sizeof(src); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { ctx->remaining -= sizeof(src); return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint8(struct pldm_msgbuf_rw *ctx, const uint8_t src) { static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(src) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(src)) { assert(ctx->cursor); memcpy(ctx->cursor, &src, sizeof(src)); ctx->cursor += sizeof(src); ctx->remaining -= sizeof(src); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { ctx->remaining -= sizeof(src); return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int32(struct pldm_msgbuf_rw *ctx, const int32_t src) { int32_t val = htole32(src); static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(src) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(src)) { assert(ctx->cursor); memcpy(ctx->cursor, &val, sizeof(val)); ctx->cursor += sizeof(src); ctx->remaining -= sizeof(src); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { ctx->remaining -= sizeof(src); return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int16(struct pldm_msgbuf_rw *ctx, const int16_t src) { int16_t val = htole16(src); static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(src) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(src)) { assert(ctx->cursor); memcpy(ctx->cursor, &val, sizeof(val)); ctx->cursor += sizeof(src); ctx->remaining -= sizeof(src); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { ctx->remaining -= sizeof(src); return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int8(struct pldm_msgbuf_rw *ctx, const int8_t src) { static_assert( // NOLINTNEXTLINE(bugprone-sizeof-expression) sizeof(src) < INTMAX_MAX, "The following addition may not uphold the runtime assertion"); if (ctx->remaining >= (intmax_t)sizeof(src)) { assert(ctx->cursor); memcpy(ctx->cursor, &src, sizeof(src)); ctx->cursor += sizeof(src); ctx->remaining -= sizeof(src); return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)sizeof(src)) { ctx->remaining -= sizeof(src); return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_WARN_UNUSED_RESULT LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_insert_array_void(struct pldm_msgbuf_rw *ctx, size_t count, const void *src, size_t src_count) { if (count > src_count) { return -EINVAL; } if (!count) { return 0; } #if INTMAX_MAX < SIZE_MAX if (count > INTMAX_MAX) { return pldm__msgbuf_rw_invalidate(ctx); } #endif if (ctx->remaining >= (intmax_t)count) { assert(ctx->cursor); memcpy(ctx->cursor, src, count); ctx->cursor += count; ctx->remaining -= (intmax_t)count; return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)count) { ctx->remaining -= (intmax_t)count; return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } LIBPLDM_CC_NONNULL LIBPLDM_CC_WARN_UNUSED_RESULT LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_array_char(struct pldm_msgbuf_rw *ctx, size_t count, const char *src, size_t src_count) { return pldm__msgbuf_insert_array_void(ctx, count, src, src_count); } LIBPLDM_CC_NONNULL LIBPLDM_CC_WARN_UNUSED_RESULT LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf_rw *ctx, size_t count, const uint8_t *src, size_t src_count) { return pldm__msgbuf_insert_array_void(ctx, count, src, src_count); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_span_required(const uint8_t **buf, intmax_t *remaining, size_t required, const void **cursor) { #if INTMAX_MAX < SIZE_MAX if (required > INTMAX_MAX) { return pldm__msgbuf_set_invalid(remaining); } #endif if (*remaining >= (intmax_t)required) { assert(*buf); if (cursor) { *cursor = *buf; } *buf += required; *remaining -= (intmax_t)required; return 0; } if (*remaining > INTMAX_MIN + (intmax_t)required) { *remaining -= (intmax_t)required; return -EOVERFLOW; } return pldm__msgbuf_set_invalid(remaining); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_ro_span_required(struct pldm_msgbuf_ro *ctx, size_t required, const void **cursor) { return pldm__msgbuf_span_required(&ctx->cursor, &ctx->remaining, required, cursor); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_rw_span_required(struct pldm_msgbuf_rw *ctx, size_t required, void **cursor) { return pldm__msgbuf_span_required((const uint8_t **)&ctx->cursor, &ctx->remaining, required, (const void **)cursor); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_span_string_ascii(const uint8_t **buf, intmax_t *remaining, const void **cursor, size_t *length) { intmax_t measured; if (*remaining < 0) { return pldm__msgbuf_set_invalid(remaining); } assert(*buf); measured = (intmax_t)strnlen((const char *)*buf, *remaining); if (measured == *remaining) { return pldm__msgbuf_set_invalid(remaining); } /* Include the NUL terminator in the span length, as spans are opaque */ measured++; if (*remaining >= measured) { assert(*buf); if (cursor) { *cursor = *buf; } *buf += measured; if (length) { *length = measured; } *remaining -= measured; return 0; } if (*remaining > INTMAX_MIN + measured) { *remaining -= measured; return -EOVERFLOW; } return pldm__msgbuf_set_invalid(remaining); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_rw_span_string_ascii(struct pldm_msgbuf_rw *ctx, void **cursor, size_t *length) { return pldm__msgbuf_span_string_ascii((const uint8_t **)&ctx->cursor, &ctx->remaining, (const void **)cursor, length); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_ro_span_string_ascii(struct pldm_msgbuf_ro *ctx, const void **cursor, size_t *length) { return pldm__msgbuf_span_string_ascii(&ctx->cursor, &ctx->remaining, cursor, length); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) int pldm__msgbuf_span_string_utf16(const uint8_t **buf, intmax_t *remaining, const void **cursor, size_t *length) { static const char16_t term = 0; ptrdiff_t measured; const void *end; if (*remaining < 0) { return pldm__msgbuf_set_invalid(remaining); } assert(*buf); /* * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do * not form a UTF16 NUL _code-point_ due to alignment with respect to the * start of the string */ end = *buf; do { if (end != *buf) { /* * If we've looped we've found a relatively-unaligned NUL code-point. * Scan again from a relatively-aligned start point. */ end = (char *)end + 1; } measured = (char *)end - (char *)*buf; end = memmem(end, *remaining - measured, &term, sizeof(term)); } while (end && ((uintptr_t)end & 1) != ((uintptr_t)*buf & 1)); if (!end) { /* * Optimistically, the last required pattern byte was one beyond the end of * the buffer. Setting ctx->remaining negative ensures the * `pldm_msgbuf_complete*()` APIs also return an error. */ return pldm__msgbuf_set_invalid(remaining); } end = (char *)end + sizeof(char16_t); measured = (char *)end - (char *)*buf; #if INTMAX_MAX < PTRDIFF_MAX if (measured >= INTMAX_MAX) { return pldm__msgbuf_set_invalid(remaining); } #endif if (*remaining >= (intmax_t)measured) { assert(*buf); if (cursor) { *cursor = *buf; } *buf += measured; if (length) { *length = (size_t)measured; } *remaining -= (intmax_t)measured; return 0; } if (*remaining > INTMAX_MIN + (intmax_t)measured) { *remaining -= (intmax_t)measured; return -EOVERFLOW; } return pldm__msgbuf_set_invalid(remaining); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_ro_span_string_utf16(struct pldm_msgbuf_ro *ctx, const void **cursor, size_t *length) { return pldm__msgbuf_span_string_utf16(&ctx->cursor, &ctx->remaining, cursor, length); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_rw_span_string_utf16(struct pldm_msgbuf_rw *ctx, void **cursor, size_t *length) { return pldm__msgbuf_span_string_utf16((const uint8_t **)&ctx->cursor, &ctx->remaining, (const void **)cursor, length); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_span_remaining(const uint8_t **buf, intmax_t *remaining, const void **cursor, size_t *len) { if (*remaining < 0) { return -EOVERFLOW; } assert(*buf); *cursor = *buf; *buf += *remaining; *len = *remaining; *remaining = 0; return 0; } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_rw_span_remaining(struct pldm_msgbuf_rw *ctx, void **cursor, size_t *len) { return pldm__msgbuf_span_remaining((const uint8_t **)&ctx->cursor, &ctx->remaining, (const void **)cursor, len); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_ro_span_remaining(struct pldm_msgbuf_ro *ctx, const void **cursor, size_t *len) { return pldm__msgbuf_span_remaining(&ctx->cursor, &ctx->remaining, cursor, len); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) int pldm__msgbuf_span_until(const uint8_t **buf, intmax_t *remaining, size_t trailer, const void **cursor, size_t *length) { #if INTMAX_MAX < SIZE_MAX if (trailer > INTMAX_MAX) { return pldm__msgbuf_set_invalid(remaining); } #endif if (*remaining >= (intmax_t)trailer) { ptrdiff_t delta; assert(*buf); delta = *remaining - (intmax_t)trailer; if (cursor) { *cursor = *buf; } *buf += delta; if (length) { *length = delta; } *remaining = (intmax_t)trailer; return 0; } if (*remaining > INTMAX_MIN + (intmax_t)trailer) { *remaining = INTMAX_MIN + (intmax_t)trailer; return -EOVERFLOW; } return pldm__msgbuf_set_invalid(remaining); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_ro_span_until(struct pldm_msgbuf_ro *ctx, size_t trailer, const void **cursor, size_t *length) { return pldm__msgbuf_span_until(&ctx->cursor, &ctx->remaining, trailer, cursor, length); } LIBPLDM_CC_NONNULL_ARGS(1) LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_rw_span_until(struct pldm_msgbuf_rw *ctx, size_t trailer, void **cursor, size_t *length) { return pldm__msgbuf_span_until((const uint8_t **)&ctx->cursor, &ctx->remaining, trailer, (const void **)cursor, length); } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_peek_remaining(struct pldm_msgbuf_rw *ctx, void **cursor, size_t *len) { if (ctx->remaining < 0) { return -EOVERFLOW; } assert(ctx->cursor); *cursor = ctx->cursor; *len = ctx->remaining; return 0; } LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_skip(struct pldm_msgbuf_rw *ctx, size_t count) { #if INTMAX_MAX < SIZE_MAX if (count > INTMAX_MAX) { return pldm__msgbuf_rw_invalidate(ctx); } #endif if (ctx->remaining >= (intmax_t)count) { assert(ctx->cursor); ctx->cursor += count; ctx->remaining -= (intmax_t)count; return 0; } if (ctx->remaining > INTMAX_MIN + (intmax_t)count) { ctx->remaining -= (intmax_t)count; return -EOVERFLOW; } return pldm__msgbuf_rw_invalidate(ctx); } /** * @brief Complete the pldm_msgbuf instance and return the number of bytes * consumed. * * @param ctx - The msgbuf. * @param orig_len - The original size of the msgbuf, the `len` argument passed to * pldm_msgbuf_init_errno(). * @param ret_used_len - The number of bytes that have been used from the msgbuf instance. * * This can be called after a number of pldm_msgbuf_insert...() calls to * determine the total size that was written. * * @return 0 on success, -EOVERFLOW if an implausible orig_len was provided or * an out-of-bounds access occurred. */ LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE LIBPLDM_CC_WARN_UNUSED_RESULT int pldm_msgbuf_complete_used(struct pldm_msgbuf_rw *ctx, size_t orig_len, size_t *ret_used_len) { int rc; ctx->cursor = NULL; rc = pldm_msgbuf_rw_validate(ctx); if (rc) { pldm__msgbuf_rw_invalidate(ctx); return rc; } if ((size_t)ctx->remaining > orig_len) { /* Caller passed incorrect orig_len */ return pldm__msgbuf_rw_invalidate(ctx); } *ret_used_len = orig_len - ctx->remaining; pldm__msgbuf_rw_invalidate(ctx); return 0; } /** * @brief pldm_msgbuf copy data between two msg buffers * * @param[in,out] src - pldm_msgbuf for source from where value should be copied * @param[in,out] dst - destination of copy from source * @param[in] size - size of data to be copied * @param[in] description - description of data copied * * @return PLDM_SUCCESS if buffer accesses were in-bounds, * PLDM_ERROR_INVALID_LENGTH otherwise. * PLDM_ERROR_INVALID_DATA if input is invalid */ #define pldm_msgbuf_copy(dst, src, type, name) \ pldm__msgbuf_copy(dst, src, sizeof(type), #name) LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_copy(struct pldm_msgbuf_rw *dst, struct pldm_msgbuf_ro *src, size_t size, const char *description LIBPLDM_CC_UNUSED) { #if INTMAX_MAX < SIZE_MAX if (size > INTMAX_MAX) { pldm__msgbuf_ro_invalidate(src); pldm__msgbuf_rw_invalidate(dst); return -EOVERFLOW; } #endif if (src->remaining >= (intmax_t)size && dst->remaining >= (intmax_t)size) { assert(src->cursor && dst->cursor); memcpy(dst->cursor, src->cursor, size); src->cursor += size; src->remaining -= (intmax_t)size; dst->cursor += size; dst->remaining -= (intmax_t)size; return 0; } if (src->remaining > INTMAX_MIN + (intmax_t)size) { src->remaining -= (intmax_t)size; } else { pldm__msgbuf_ro_invalidate(src); } if (dst->remaining > INTMAX_MIN + (intmax_t)size) { dst->remaining -= (intmax_t)size; } else { pldm__msgbuf_rw_invalidate(dst); } return -EOVERFLOW; } LIBPLDM_CC_NONNULL LIBPLDM_CC_WARN_UNUSED_RESULT LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf_rw *dst, struct pldm_msgbuf_ro *src) { const void *ascii = NULL; size_t len = 0; int rc; rc = pldm_msgbuf_ro_span_string_ascii(src, &ascii, &len); if (rc < 0) { return rc; } return pldm__msgbuf_insert_array_void(dst, len, ascii, len); } LIBPLDM_CC_NONNULL LIBPLDM_CC_WARN_UNUSED_RESULT LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf_rw *dst, struct pldm_msgbuf_ro *src) { const void *utf16 = NULL; size_t len = 0; int rc; rc = pldm_msgbuf_ro_span_string_utf16(src, &utf16, &len); if (rc < 0) { return rc; } return pldm__msgbuf_insert_array_void(dst, len, utf16, len); } /** * @brief pldm_msgbuf uint8_t extractor for a size_t * * @param[in,out] ctx - pldm_msgbuf context for extractor * @param[out] dst - destination of extracted value * * @return 0 if buffer accesses were in-bounds, * -EINVAL if dst pointer is invalid, * -EOVERFLOW is the buffer was out of bound. */ #define pldm_msgbuf_extract_uint8_to_size(ctx, dst) \ pldm__msgbuf_extract_uint8_to_size(ctx, &(dst)) LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_uint8_to_size(struct pldm_msgbuf_ro *ctx, size_t *dst) { uint8_t value = 0; int rc; rc = pldm__msgbuf_extract_uint8(ctx, &value); if (rc) { return rc; } static_assert(SIZE_MAX >= UINT8_MAX, "Invalid promotion"); *dst = value; return 0; } /** * @brief pldm_msgbuf uint16_t extractor for a size_t * * @param[in,out] ctx - pldm_msgbuf context for extractor * @param[out] dst - destination of extracted value * * @return 0 if buffer accesses were in-bounds, * -EINVAL if dst pointer is invalid, * -EOVERFLOW is the buffer was out of bound. */ #define pldm_msgbuf_extract_uint16_to_size(ctx, dst) \ pldm__msgbuf_extract_uint16_to_size(ctx, &(dst)) LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_uint16_to_size(struct pldm_msgbuf_ro *ctx, size_t *dst) { uint16_t value = 0; int rc; rc = pldm__msgbuf_extract_uint16(ctx, &value); if (rc) { return rc; } static_assert(SIZE_MAX >= UINT16_MAX, "Invalid promotion"); *dst = value; return 0; } /** * @brief pldm_msgbuf uint32_t extractor for a size_t * * @param[in,out] ctx - pldm_msgbuf context for extractor * @param[out] dst - destination of extracted value * * @return 0 if buffer accesses were in-bounds, * -EINVAL if dst pointer is invalid, * -EOVERFLOW is the buffer was out of bound. */ #define pldm_msgbuf_extract_uint32_to_size(ctx, dst) \ pldm__msgbuf_extract_uint32_to_size(ctx, &(dst)) LIBPLDM_CC_NONNULL LIBPLDM_CC_ALWAYS_INLINE int // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) pldm__msgbuf_extract_uint32_to_size(struct pldm_msgbuf_ro *ctx, size_t *dst) { uint32_t value = 0; int rc; rc = pldm__msgbuf_extract_uint32(ctx, &value); if (rc) { return rc; } static_assert(SIZE_MAX >= UINT32_MAX, "Invalid promotion"); *dst = value; return 0; } #ifdef __cplusplus } #endif #endif