1691668feSPatrick Williams /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2c63f63a2SAndrew Jeffery #ifndef PLDM_MSGBUF_H 3c63f63a2SAndrew Jeffery #define PLDM_MSGBUF_H 4c63f63a2SAndrew Jeffery 566c7723aSAndrew Jeffery /* 666c7723aSAndrew Jeffery * Historically, many of the structs exposed in libpldm's public headers are 766c7723aSAndrew Jeffery * defined with __attribute__((packed)). This is unfortunate: it gives the 866c7723aSAndrew Jeffery * impression that a wire-format buffer can be cast to the message type to make 966c7723aSAndrew Jeffery * the message's fields easily accessible. As it turns out, that's not 1066c7723aSAndrew Jeffery * that's valid for several reasons: 1166c7723aSAndrew Jeffery * 1266c7723aSAndrew Jeffery * 1. Casting the wire-format buffer to a struct of the message type doesn't 1366c7723aSAndrew Jeffery * abstract the endianness of message field values 1466c7723aSAndrew Jeffery * 1566c7723aSAndrew Jeffery * 2. Some messages contain packed tagged union fields which cannot be properly 1666c7723aSAndrew Jeffery * described in a C struct. 1766c7723aSAndrew Jeffery * 1866c7723aSAndrew Jeffery * The msgbuf APIs exist to assist with (un)packing the wire-format in a way 1966c7723aSAndrew Jeffery * that is type-safe, spatially memory-safe, endian-safe, performant, and 2066c7723aSAndrew Jeffery * free of undefined-behaviour. Message structs that are added to the public 2166c7723aSAndrew Jeffery * library API should no-longer be marked __attribute__((packed)), and the 2266c7723aSAndrew Jeffery * implementation of their encode and decode functions must exploit the msgbuf 2366c7723aSAndrew Jeffery * API. 2466c7723aSAndrew Jeffery * 2566c7723aSAndrew Jeffery * However, we would like to allow implementation of codec functions in terms of 2666c7723aSAndrew Jeffery * msgbuf APIs even if they're decoding a message into a (historically) packed 2766c7723aSAndrew Jeffery * struct. Some of the complexity that follows is a consequence of the packed/ 2866c7723aSAndrew Jeffery * unpacked conflict. 2966c7723aSAndrew Jeffery */ 3066c7723aSAndrew Jeffery 31c63f63a2SAndrew Jeffery #ifdef __cplusplus 3237dd6a3dSAndrew Jeffery /* 3337dd6a3dSAndrew Jeffery * Fix up C11's _Static_assert() vs C++'s static_assert(). 3437dd6a3dSAndrew Jeffery * 3537dd6a3dSAndrew Jeffery * Can we please have nice things for once. 3637dd6a3dSAndrew Jeffery */ 3737dd6a3dSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 3837dd6a3dSAndrew Jeffery #define _Static_assert(...) static_assert(__VA_ARGS__) 39c63f63a2SAndrew Jeffery extern "C" { 40c63f63a2SAndrew Jeffery #endif 41c63f63a2SAndrew Jeffery 42b0c1d20aSAndrew Jeffery #include <libpldm/base.h> 43b0c1d20aSAndrew Jeffery #include <libpldm/pldm_types.h> 44c63f63a2SAndrew Jeffery 4566c7723aSAndrew Jeffery #include "compiler.h" 4666c7723aSAndrew Jeffery 47c63f63a2SAndrew Jeffery #include <assert.h> 48c63f63a2SAndrew Jeffery #include <endian.h> 49c63f63a2SAndrew Jeffery #include <limits.h> 50c63f63a2SAndrew Jeffery #include <stdbool.h> 5166c7723aSAndrew Jeffery #include <stdint.h> 52c63f63a2SAndrew Jeffery #include <string.h> 53c63f63a2SAndrew Jeffery #include <sys/types.h> 54c63f63a2SAndrew Jeffery 55*2ff8cf89SAndrew Jeffery /* 56*2ff8cf89SAndrew Jeffery * We can't use static_assert() outside of some other C construct. Deal 57*2ff8cf89SAndrew Jeffery * with high-level global assertions by burying them in an unused struct 58*2ff8cf89SAndrew Jeffery * declaration, that has a sole member for compliance with the requirement that 59*2ff8cf89SAndrew Jeffery * types must have a size. 60*2ff8cf89SAndrew Jeffery */ 61*2ff8cf89SAndrew Jeffery static struct { 62*2ff8cf89SAndrew Jeffery static_assert( 63*2ff8cf89SAndrew Jeffery INTMAX_MAX != SIZE_MAX, 64*2ff8cf89SAndrew Jeffery "Extraction and insertion value comparisons may be broken"); 65*2ff8cf89SAndrew Jeffery static_assert(INTMAX_MIN + INTMAX_MAX <= 0, 66*2ff8cf89SAndrew Jeffery "Extraction and insertion arithmetic may be broken"); 67*2ff8cf89SAndrew Jeffery int compliance; 68*2ff8cf89SAndrew Jeffery } build_assertions __attribute__((unused)); 69*2ff8cf89SAndrew Jeffery 70c63f63a2SAndrew Jeffery struct pldm_msgbuf { 71062c8762SThu Nguyen uint8_t *cursor; 72*2ff8cf89SAndrew Jeffery intmax_t remaining; 73c63f63a2SAndrew Jeffery }; 74c63f63a2SAndrew Jeffery 75c63f63a2SAndrew Jeffery /** 76c63f63a2SAndrew Jeffery * @brief Initialize pldm buf struct for buf extractor 77c63f63a2SAndrew Jeffery * 78c63f63a2SAndrew Jeffery * @param[out] ctx - pldm_msgbuf context for extractor 79c63f63a2SAndrew Jeffery * @param[in] minsize - The minimum required length of buffer `buf` 80c63f63a2SAndrew Jeffery * @param[in] buf - buffer to be extracted 81c63f63a2SAndrew Jeffery * @param[in] len - size of buffer 82c63f63a2SAndrew Jeffery * 83c63f63a2SAndrew Jeffery * @return PLDM_SUCCESS if all buffer accesses were in-bounds, 84c63f63a2SAndrew Jeffery * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or 85c63f63a2SAndrew Jeffery * PLDM_ERROR_INVALID_LENGTH if length constraints are violated. 86c63f63a2SAndrew Jeffery */ 8707febdbbSAndrew Jeffery static inline int pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, 8807febdbbSAndrew Jeffery const void *buf, size_t len) 89c63f63a2SAndrew Jeffery { 90c63f63a2SAndrew Jeffery if (!ctx || !buf) { 91c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 92c63f63a2SAndrew Jeffery } 93c63f63a2SAndrew Jeffery 94*2ff8cf89SAndrew Jeffery if ((minsize > len)) { 95c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 96c63f63a2SAndrew Jeffery } 97c63f63a2SAndrew Jeffery 98*2ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX 99*2ff8cf89SAndrew Jeffery if (len > INTMAX_MAX) { 100*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 101*2ff8cf89SAndrew Jeffery } 102*2ff8cf89SAndrew Jeffery #endif 103*2ff8cf89SAndrew Jeffery 10407febdbbSAndrew Jeffery if ((uintptr_t)buf + len < len) { 105c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 106c63f63a2SAndrew Jeffery } 107c63f63a2SAndrew Jeffery 108c63f63a2SAndrew Jeffery ctx->cursor = (uint8_t *)buf; 109*2ff8cf89SAndrew Jeffery ctx->remaining = (intmax_t)len; 110c63f63a2SAndrew Jeffery 111c63f63a2SAndrew Jeffery return PLDM_SUCCESS; 112c63f63a2SAndrew Jeffery } 113c63f63a2SAndrew Jeffery 114c63f63a2SAndrew Jeffery /** 115c63f63a2SAndrew Jeffery * @brief Validate buffer overflow state 116c63f63a2SAndrew Jeffery * 117c63f63a2SAndrew Jeffery * @param[in] ctx - pldm_msgbuf context for extractor 118c63f63a2SAndrew Jeffery * 119c63f63a2SAndrew Jeffery * @return PLDM_SUCCESS if there are zero or more bytes of data that remain 120c63f63a2SAndrew Jeffery * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a 121c63f63a2SAndrew Jeffery * prior accesses would have occurred beyond the bounds of the buffer, and 122c63f63a2SAndrew Jeffery * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 123c63f63a2SAndrew Jeffery * pointer. 124c63f63a2SAndrew Jeffery */ 125c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx) 126c63f63a2SAndrew Jeffery { 127c63f63a2SAndrew Jeffery if (!ctx) { 128c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 129c63f63a2SAndrew Jeffery } 130c63f63a2SAndrew Jeffery 131c63f63a2SAndrew Jeffery return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH; 132c63f63a2SAndrew Jeffery } 133c63f63a2SAndrew Jeffery 134c63f63a2SAndrew Jeffery /** 135db7b8324SAndrew Jeffery * @brief Test whether a message buffer has been exactly consumed 136db7b8324SAndrew Jeffery * 137db7b8324SAndrew Jeffery * @param[in] ctx - pldm_msgbuf context for extractor 138db7b8324SAndrew Jeffery * 139db7b8324SAndrew Jeffery * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from 140db7b8324SAndrew Jeffery * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH 141db7b8324SAndrew Jeffery * indicates that an incorrect sequence of accesses have occurred, and 142db7b8324SAndrew Jeffery * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid 143db7b8324SAndrew Jeffery * pointer. 144db7b8324SAndrew Jeffery */ 145db7b8324SAndrew Jeffery static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx) 146db7b8324SAndrew Jeffery { 147db7b8324SAndrew Jeffery if (!ctx) { 148db7b8324SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 149db7b8324SAndrew Jeffery } 150db7b8324SAndrew Jeffery 151db7b8324SAndrew Jeffery return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH; 152db7b8324SAndrew Jeffery } 153db7b8324SAndrew Jeffery 154db7b8324SAndrew Jeffery /** 155c63f63a2SAndrew Jeffery * @brief Destroy the pldm buf 156c63f63a2SAndrew Jeffery * 157c63f63a2SAndrew Jeffery * @param[in] ctx - pldm_msgbuf context for extractor 158c63f63a2SAndrew Jeffery * 159c63f63a2SAndrew Jeffery * @return PLDM_SUCCESS if all buffer accesses were in-bounds, 160c63f63a2SAndrew Jeffery * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or 161c63f63a2SAndrew Jeffery * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the 162c63f63a2SAndrew Jeffery * bounds of the buffer. 163c63f63a2SAndrew Jeffery */ 164c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx) 165c63f63a2SAndrew Jeffery { 166c63f63a2SAndrew Jeffery int valid; 167c63f63a2SAndrew Jeffery 168c63f63a2SAndrew Jeffery if (!ctx) { 169c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 170c63f63a2SAndrew Jeffery } 171c63f63a2SAndrew Jeffery 172c63f63a2SAndrew Jeffery valid = pldm_msgbuf_validate(ctx); 173c63f63a2SAndrew Jeffery 174c63f63a2SAndrew Jeffery ctx->cursor = NULL; 175c63f63a2SAndrew Jeffery ctx->remaining = 0; 176c63f63a2SAndrew Jeffery 177c63f63a2SAndrew Jeffery return valid; 178c63f63a2SAndrew Jeffery } 179c63f63a2SAndrew Jeffery 180c63f63a2SAndrew Jeffery /** 181db7b8324SAndrew Jeffery * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer 182db7b8324SAndrew Jeffery * has been completely consumed without overflow 183db7b8324SAndrew Jeffery * 184db7b8324SAndrew Jeffery * @param[in] ctx - pldm_msgbuf context 185db7b8324SAndrew Jeffery * 186db7b8324SAndrew Jeffery * @return PLDM_SUCCESS if all buffer access were in-bounds and completely 187db7b8324SAndrew Jeffery * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx 188db7b8324SAndrew Jeffery * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would 189db7b8324SAndrew Jeffery * have occurred byond the bounds of the buffer 190db7b8324SAndrew Jeffery */ 191db7b8324SAndrew Jeffery static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx) 192db7b8324SAndrew Jeffery { 193db7b8324SAndrew Jeffery int consumed; 194db7b8324SAndrew Jeffery 195db7b8324SAndrew Jeffery if (!ctx) { 196db7b8324SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 197db7b8324SAndrew Jeffery } 198db7b8324SAndrew Jeffery 199db7b8324SAndrew Jeffery consumed = pldm_msgbuf_consumed(ctx); 200db7b8324SAndrew Jeffery 201db7b8324SAndrew Jeffery ctx->cursor = NULL; 202db7b8324SAndrew Jeffery ctx->remaining = 0; 203db7b8324SAndrew Jeffery 204db7b8324SAndrew Jeffery return consumed; 205db7b8324SAndrew Jeffery } 206db7b8324SAndrew Jeffery 20766c7723aSAndrew Jeffery /* 20866c7723aSAndrew Jeffery * Exploit the pre-processor to perform type checking by macro substitution. 20966c7723aSAndrew Jeffery * 21066c7723aSAndrew Jeffery * A C type is defined by its alignment as well as its object 21166c7723aSAndrew Jeffery * size, and compilers have a hammer to enforce it in the form of 21266c7723aSAndrew Jeffery * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in 21366c7723aSAndrew Jeffery * the libpldm public API this presents a problem: Naively attempting to use the 21466c7723aSAndrew Jeffery * msgbuf APIs on a member of a packed struct would yield an error. 21566c7723aSAndrew Jeffery * 21666c7723aSAndrew Jeffery * The msgbuf APIs are implemented such that data is moved through unaligned 21766c7723aSAndrew Jeffery * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must 21866c7723aSAndrew Jeffery * make the object pointers take a trip through `void *` at its API boundary. 21966c7723aSAndrew Jeffery * That presents a bit too much of an opportunity to non-surgically remove your 22066c7723aSAndrew Jeffery * own foot, so here we set about doing something to mitigate that as well. 22166c7723aSAndrew Jeffery * 22266c7723aSAndrew Jeffery * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness 22366c7723aSAndrew Jeffery * only for the purpose of object sizes, disregarding alignment. We have a few 22466c7723aSAndrew Jeffery * constraints that cause some headaches: 22566c7723aSAndrew Jeffery * 22666c7723aSAndrew Jeffery * 1. We have to perform the type-check before a call through a C function, 22766c7723aSAndrew Jeffery * as the function must take the object pointer argument as `void *`. 22866c7723aSAndrew Jeffery * Essentially, this constrains us to doing something with macros. 22966c7723aSAndrew Jeffery * 23066c7723aSAndrew Jeffery * 2. While libpldm is a C library, its test suite is written in C++ to take 23166c7723aSAndrew Jeffery * advantage of gtest. 23266c7723aSAndrew Jeffery * 23366c7723aSAndrew Jeffery * 3. Ideally we'd do something with C's `static_assert()`, however 23466c7723aSAndrew Jeffery * `static_assert()` is defined as void, and as we're constrained to macros, 23566c7723aSAndrew Jeffery * using `static_assert()` would require a statement-expression 23666c7723aSAndrew Jeffery * 23766c7723aSAndrew Jeffery * 4. Currently the project is built with `-std=c17`. CPP statement-expressions 23866c7723aSAndrew Jeffery * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for 23966c7723aSAndrew Jeffery * the purpose of enabling statement-expressions in this one instance. 24066c7723aSAndrew Jeffery * 24166c7723aSAndrew Jeffery * 5. We can achieve a conditional build error using `pldm_require_obj_type()`, 24266c7723aSAndrew Jeffery * however it's implemented in terms of `_Generic()`, which is not available 24366c7723aSAndrew Jeffery * in C++. 24466c7723aSAndrew Jeffery * 24566c7723aSAndrew Jeffery * Combined this means we need separate solutions for C and C++. 24666c7723aSAndrew Jeffery * 24766c7723aSAndrew Jeffery * For C, as we don't have statement-expressions, we need to exploit some other 24866c7723aSAndrew Jeffery * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf 24966c7723aSAndrew Jeffery * API function call. We also have to take care of the fact that the call-sites 25066c7723aSAndrew Jeffery * may be in the context of a variable assignment for error-handling purposes. 25166c7723aSAndrew Jeffery * The key observation is that we can use the comma operator as a sequence point 25266c7723aSAndrew Jeffery * to order the type check before the API call, discarding the "result" value of 25366c7723aSAndrew Jeffery * the type check and yielding the return value of the API call. 25466c7723aSAndrew Jeffery * 25566c7723aSAndrew Jeffery * C++ could be less of a headache than the C as we can leverage template 25666c7723aSAndrew Jeffery * functions. An advantage of template functions is that while their definition 25766c7723aSAndrew Jeffery * is driven by instantion, the definition does not appear at the source 25866c7723aSAndrew Jeffery * location of the instantation, which gives it a great leg-up over the problems 25966c7723aSAndrew Jeffery * we have in the C path. However, the use of the msgbuf APIs in the test suite 26066c7723aSAndrew Jeffery * still makes things somewhat tricky, as the call-sites in the test suite are 26166c7723aSAndrew Jeffery * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that 26266c7723aSAndrew Jeffery * takes both the object type and the required type as template arguments, and 26366c7723aSAndrew Jeffery * then define the object pointer parameter as `void *` for a call through to 26466c7723aSAndrew Jeffery * the appropriate msgbuf API. However, because the msgbuf API call-sites are 26566c7723aSAndrew Jeffery * encapsulated in gtest macros, use of commas in the template specification 26666c7723aSAndrew Jeffery * causes pre-processor confusion. In this way we're constrained to only one 26766c7723aSAndrew Jeffery * template argument per function. 26866c7723aSAndrew Jeffery * 26966c7723aSAndrew Jeffery * Implement the C++ path using template functions that take the destination 27066c7723aSAndrew Jeffery * object type as a template argument, while the name of the function symbols 27166c7723aSAndrew Jeffery * are derived from the required type. The manual implementations of these 27266c7723aSAndrew Jeffery * appear at the end of the header. The type safety is actually enforced 27366c7723aSAndrew Jeffery * by `static_assert()` this time, as we can use statements as we're not 27466c7723aSAndrew Jeffery * constrained to an expression in the templated function body. 27566c7723aSAndrew Jeffery * 27666c7723aSAndrew Jeffery * The invocations of pldm_msgbuf_extract_typecheck() typically result in 27766c7723aSAndrew Jeffery * double-evaluation of some arguments. We're not yet bothered by this for two 27866c7723aSAndrew Jeffery * reasons: 27966c7723aSAndrew Jeffery * 28066c7723aSAndrew Jeffery * 1. The nature of the current call-sites are such that there are no 28166c7723aSAndrew Jeffery * argument expressions that result in undesirable side-effects 28266c7723aSAndrew Jeffery * 28366c7723aSAndrew Jeffery * 2. It's an API internal to the libpldm implementation, and we can fix things 28466c7723aSAndrew Jeffery * whenever something crops up the violates the observation in 1. 28566c7723aSAndrew Jeffery */ 28666c7723aSAndrew Jeffery #ifdef __cplusplus 28766c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \ 28866c7723aSAndrew Jeffery pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__) 28966c7723aSAndrew Jeffery #else 29066c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \ 29166c7723aSAndrew Jeffery (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__)) 29266c7723aSAndrew Jeffery #endif 29366c7723aSAndrew Jeffery 294db7b8324SAndrew Jeffery /** 295c63f63a2SAndrew Jeffery * @brief pldm_msgbuf extractor for a uint8_t 296c63f63a2SAndrew Jeffery * 297c63f63a2SAndrew Jeffery * @param[inout] ctx - pldm_msgbuf context for extractor 298c63f63a2SAndrew Jeffery * @param[out] dst - destination of extracted value 299c63f63a2SAndrew Jeffery * 300c63f63a2SAndrew Jeffery * @return PLDM_SUCCESS if buffer accesses were in-bounds, 301c63f63a2SAndrew Jeffery * PLDM_ERROR_INVALID_LENGTH otherwise. 302c63f63a2SAndrew Jeffery * PLDM_ERROR_INVALID_DATA if input a invalid ctx 303c63f63a2SAndrew Jeffery */ 30466c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint8(ctx, dst) \ 30566c7723aSAndrew Jeffery pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \ 30666c7723aSAndrew Jeffery dst, ctx, dst) 30766c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 30866c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst) 309c63f63a2SAndrew Jeffery { 310c63f63a2SAndrew Jeffery if (!ctx || !ctx->cursor || !dst) { 311c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 312c63f63a2SAndrew Jeffery } 313c63f63a2SAndrew Jeffery 314*2ff8cf89SAndrew Jeffery if (ctx->remaining == INTMAX_MIN) { 315*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 316*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 317*2ff8cf89SAndrew Jeffery } 31866c7723aSAndrew Jeffery ctx->remaining -= sizeof(uint8_t); 319c63f63a2SAndrew Jeffery assert(ctx->remaining >= 0); 320c63f63a2SAndrew Jeffery if (ctx->remaining < 0) { 321c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 322c63f63a2SAndrew Jeffery } 323c63f63a2SAndrew Jeffery 32466c7723aSAndrew Jeffery memcpy(dst, ctx->cursor, sizeof(uint8_t)); 32566c7723aSAndrew Jeffery 326c63f63a2SAndrew Jeffery ctx->cursor++; 327c63f63a2SAndrew Jeffery return PLDM_SUCCESS; 328c63f63a2SAndrew Jeffery } 329c63f63a2SAndrew Jeffery 33066c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int8(ctx, dst) \ 33166c7723aSAndrew Jeffery pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \ 33266c7723aSAndrew Jeffery ctx, dst) 33366c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 33466c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst) 335c63f63a2SAndrew Jeffery { 336c63f63a2SAndrew Jeffery if (!ctx || !ctx->cursor || !dst) { 337c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 338c63f63a2SAndrew Jeffery } 339c63f63a2SAndrew Jeffery 340*2ff8cf89SAndrew Jeffery if (ctx->remaining == INTMAX_MIN) { 341*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 342*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 343*2ff8cf89SAndrew Jeffery } 34466c7723aSAndrew Jeffery ctx->remaining -= sizeof(int8_t); 345c63f63a2SAndrew Jeffery assert(ctx->remaining >= 0); 346c63f63a2SAndrew Jeffery if (ctx->remaining < 0) { 347c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 348c63f63a2SAndrew Jeffery } 349c63f63a2SAndrew Jeffery 35066c7723aSAndrew Jeffery memcpy(dst, ctx->cursor, sizeof(int8_t)); 351c63f63a2SAndrew Jeffery ctx->cursor++; 352c63f63a2SAndrew Jeffery return PLDM_SUCCESS; 353c63f63a2SAndrew Jeffery } 354c63f63a2SAndrew Jeffery 35566c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint16(ctx, dst) \ 35666c7723aSAndrew Jeffery pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \ 35766c7723aSAndrew Jeffery dst, ctx, dst) 35866c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 35966c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, 36066c7723aSAndrew Jeffery void *dst) 361c63f63a2SAndrew Jeffery { 362c63f63a2SAndrew Jeffery uint16_t ldst; 363c63f63a2SAndrew Jeffery 364c63f63a2SAndrew Jeffery if (!ctx || !ctx->cursor || !dst) { 365c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 366c63f63a2SAndrew Jeffery } 367c63f63a2SAndrew Jeffery 368*2ff8cf89SAndrew Jeffery // Check for underflow while tracking the magnitude of the buffer overflow 369*2ff8cf89SAndrew Jeffery static_assert( 370*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 371*2ff8cf89SAndrew Jeffery sizeof(ldst) < INTMAX_MAX, 372*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 373*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 374*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 375*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 376*2ff8cf89SAndrew Jeffery } 377*2ff8cf89SAndrew Jeffery 378c63f63a2SAndrew Jeffery // Check for buffer overflow. If we overflow, account for the request as 379c63f63a2SAndrew Jeffery // negative values in ctx->remaining. This way we can debug how far 380c63f63a2SAndrew Jeffery // we've overflowed. 381c63f63a2SAndrew Jeffery ctx->remaining -= sizeof(ldst); 382c63f63a2SAndrew Jeffery 383c63f63a2SAndrew Jeffery // Prevent the access if it would overflow. First, assert so we blow up 384c63f63a2SAndrew Jeffery // the test suite right at the point of failure. However, cater to 385c63f63a2SAndrew Jeffery // -DNDEBUG by explicitly testing that the access is valid. 386c63f63a2SAndrew Jeffery assert(ctx->remaining >= 0); 387c63f63a2SAndrew Jeffery if (ctx->remaining < 0) { 388c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 389c63f63a2SAndrew Jeffery } 390c63f63a2SAndrew Jeffery 391c63f63a2SAndrew Jeffery // Use memcpy() to have the compiler deal with any alignment 392c63f63a2SAndrew Jeffery // issues on the target architecture 393c63f63a2SAndrew Jeffery memcpy(&ldst, ctx->cursor, sizeof(ldst)); 394c63f63a2SAndrew Jeffery 395c63f63a2SAndrew Jeffery // Only assign the target value once it's correctly decoded 39666c7723aSAndrew Jeffery ldst = le16toh(ldst); 39766c7723aSAndrew Jeffery 39866c7723aSAndrew Jeffery // Allow storing to unaligned 39966c7723aSAndrew Jeffery memcpy(dst, &ldst, sizeof(ldst)); 40066c7723aSAndrew Jeffery 401c63f63a2SAndrew Jeffery ctx->cursor += sizeof(ldst); 402c63f63a2SAndrew Jeffery 403c63f63a2SAndrew Jeffery return PLDM_SUCCESS; 404c63f63a2SAndrew Jeffery } 405c63f63a2SAndrew Jeffery 40666c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int16(ctx, dst) \ 40766c7723aSAndrew Jeffery pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \ 40866c7723aSAndrew Jeffery dst, ctx, dst) 40966c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 41066c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst) 411c63f63a2SAndrew Jeffery { 412c63f63a2SAndrew Jeffery int16_t ldst; 413c63f63a2SAndrew Jeffery 414c63f63a2SAndrew Jeffery if (!ctx || !ctx->cursor || !dst) { 415c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 416c63f63a2SAndrew Jeffery } 417c63f63a2SAndrew Jeffery 418*2ff8cf89SAndrew Jeffery static_assert( 419*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 420*2ff8cf89SAndrew Jeffery sizeof(ldst) < INTMAX_MAX, 421*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 422*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 423*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 424*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 425*2ff8cf89SAndrew Jeffery } 426c63f63a2SAndrew Jeffery ctx->remaining -= sizeof(ldst); 427c63f63a2SAndrew Jeffery assert(ctx->remaining >= 0); 428c63f63a2SAndrew Jeffery if (ctx->remaining < 0) { 429c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 430c63f63a2SAndrew Jeffery } 431c63f63a2SAndrew Jeffery 432c63f63a2SAndrew Jeffery memcpy(&ldst, ctx->cursor, sizeof(ldst)); 433c63f63a2SAndrew Jeffery 43466c7723aSAndrew Jeffery ldst = le16toh(ldst); 43566c7723aSAndrew Jeffery memcpy(dst, &ldst, sizeof(ldst)); 436c63f63a2SAndrew Jeffery ctx->cursor += sizeof(ldst); 437c63f63a2SAndrew Jeffery 438c63f63a2SAndrew Jeffery return PLDM_SUCCESS; 439c63f63a2SAndrew Jeffery } 440c63f63a2SAndrew Jeffery 44166c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint32(ctx, dst) \ 44266c7723aSAndrew Jeffery pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \ 44366c7723aSAndrew Jeffery dst, ctx, dst) 44466c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 44566c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, 44666c7723aSAndrew Jeffery void *dst) 447c63f63a2SAndrew Jeffery { 448c63f63a2SAndrew Jeffery uint32_t ldst; 449c63f63a2SAndrew Jeffery 450c63f63a2SAndrew Jeffery if (!ctx || !ctx->cursor || !dst) { 451c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 452c63f63a2SAndrew Jeffery } 453c63f63a2SAndrew Jeffery 454*2ff8cf89SAndrew Jeffery static_assert( 455*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 456*2ff8cf89SAndrew Jeffery sizeof(ldst) < INTMAX_MAX, 457*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 458*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 459*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 460*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 461*2ff8cf89SAndrew Jeffery } 462c63f63a2SAndrew Jeffery ctx->remaining -= sizeof(ldst); 463c63f63a2SAndrew Jeffery assert(ctx->remaining >= 0); 464c63f63a2SAndrew Jeffery if (ctx->remaining < 0) { 465c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 466c63f63a2SAndrew Jeffery } 467c63f63a2SAndrew Jeffery 468c63f63a2SAndrew Jeffery memcpy(&ldst, ctx->cursor, sizeof(ldst)); 469c63f63a2SAndrew Jeffery 47066c7723aSAndrew Jeffery ldst = le32toh(ldst); 47166c7723aSAndrew Jeffery memcpy(dst, &ldst, sizeof(ldst)); 472c63f63a2SAndrew Jeffery ctx->cursor += sizeof(ldst); 473c63f63a2SAndrew Jeffery 474c63f63a2SAndrew Jeffery return PLDM_SUCCESS; 475c63f63a2SAndrew Jeffery } 476c63f63a2SAndrew Jeffery 47766c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int32(ctx, dst) \ 47866c7723aSAndrew Jeffery pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \ 47966c7723aSAndrew Jeffery dst, ctx, dst) 48066c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 48166c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst) 482c63f63a2SAndrew Jeffery { 483c63f63a2SAndrew Jeffery int32_t ldst; 484c63f63a2SAndrew Jeffery 485c63f63a2SAndrew Jeffery if (!ctx || !ctx->cursor || !dst) { 486c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 487c63f63a2SAndrew Jeffery } 488c63f63a2SAndrew Jeffery 489*2ff8cf89SAndrew Jeffery static_assert( 490*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 491*2ff8cf89SAndrew Jeffery sizeof(ldst) < INTMAX_MAX, 492*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 493*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 494*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 495*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 496*2ff8cf89SAndrew Jeffery } 497c63f63a2SAndrew Jeffery ctx->remaining -= sizeof(ldst); 498c63f63a2SAndrew Jeffery assert(ctx->remaining >= 0); 499c63f63a2SAndrew Jeffery if (ctx->remaining < 0) { 500c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 501c63f63a2SAndrew Jeffery } 502c63f63a2SAndrew Jeffery 503c63f63a2SAndrew Jeffery memcpy(&ldst, ctx->cursor, sizeof(ldst)); 504c63f63a2SAndrew Jeffery 50566c7723aSAndrew Jeffery ldst = le32toh(ldst); 50666c7723aSAndrew Jeffery memcpy(dst, &ldst, sizeof(ldst)); 507c63f63a2SAndrew Jeffery ctx->cursor += sizeof(ldst); 508c63f63a2SAndrew Jeffery 509c63f63a2SAndrew Jeffery return PLDM_SUCCESS; 510c63f63a2SAndrew Jeffery } 511c63f63a2SAndrew Jeffery 51266c7723aSAndrew Jeffery #define pldm_msgbuf_extract_real32(ctx, dst) \ 51366c7723aSAndrew Jeffery pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \ 51466c7723aSAndrew Jeffery dst, ctx, dst) 51566c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 51666c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, 51766c7723aSAndrew Jeffery void *dst) 518c63f63a2SAndrew Jeffery { 519c63f63a2SAndrew Jeffery uint32_t ldst; 520c63f63a2SAndrew Jeffery 52166c7723aSAndrew Jeffery _Static_assert(sizeof(real32_t) == sizeof(ldst), 52266c7723aSAndrew Jeffery "Mismatched type sizes for dst and ldst"); 52366c7723aSAndrew Jeffery 524c63f63a2SAndrew Jeffery if (!ctx || !ctx->cursor || !dst) { 525c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 526c63f63a2SAndrew Jeffery } 527c63f63a2SAndrew Jeffery 528*2ff8cf89SAndrew Jeffery static_assert( 529*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 530*2ff8cf89SAndrew Jeffery sizeof(ldst) < INTMAX_MAX, 531*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 532*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) { 533*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 534*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 535*2ff8cf89SAndrew Jeffery } 536c63f63a2SAndrew Jeffery ctx->remaining -= sizeof(ldst); 537c63f63a2SAndrew Jeffery assert(ctx->remaining >= 0); 538c63f63a2SAndrew Jeffery if (ctx->remaining < 0) { 539c63f63a2SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 540c63f63a2SAndrew Jeffery } 541c63f63a2SAndrew Jeffery 542c63f63a2SAndrew Jeffery memcpy(&ldst, ctx->cursor, sizeof(ldst)); 543c63f63a2SAndrew Jeffery ldst = le32toh(ldst); 54466c7723aSAndrew Jeffery memcpy(dst, &ldst, sizeof(ldst)); 54566c7723aSAndrew Jeffery ctx->cursor += sizeof(ldst); 546c63f63a2SAndrew Jeffery 547c63f63a2SAndrew Jeffery return PLDM_SUCCESS; 548c63f63a2SAndrew Jeffery } 549c63f63a2SAndrew Jeffery 55066c7723aSAndrew Jeffery /** 55166c7723aSAndrew Jeffery * Extract the field at the msgbuf cursor into the lvalue named by dst. 55266c7723aSAndrew Jeffery * 55366c7723aSAndrew Jeffery * @param ctx The msgbuf context object 55466c7723aSAndrew Jeffery * @param dst The lvalue into which the field at the msgbuf cursor should be 55566c7723aSAndrew Jeffery * extracted 55666c7723aSAndrew Jeffery * 55766c7723aSAndrew Jeffery * @return PLDM_SUCCESS on success, otherwise another value on error 55866c7723aSAndrew Jeffery */ 559c63f63a2SAndrew Jeffery #define pldm_msgbuf_extract(ctx, dst) \ 56066c7723aSAndrew Jeffery _Generic((dst), \ 56166c7723aSAndrew Jeffery uint8_t: pldm__msgbuf_extract_uint8, \ 56266c7723aSAndrew Jeffery int8_t: pldm__msgbuf_extract_int8, \ 56366c7723aSAndrew Jeffery uint16_t: pldm__msgbuf_extract_uint16, \ 56466c7723aSAndrew Jeffery int16_t: pldm__msgbuf_extract_int16, \ 56566c7723aSAndrew Jeffery uint32_t: pldm__msgbuf_extract_uint32, \ 56666c7723aSAndrew Jeffery int32_t: pldm__msgbuf_extract_int32, \ 56766c7723aSAndrew Jeffery real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst)) 56866c7723aSAndrew Jeffery 56966c7723aSAndrew Jeffery /** 57066c7723aSAndrew Jeffery * Extract the field at the msgbuf cursor into the object pointed-to by dst. 57166c7723aSAndrew Jeffery * 57266c7723aSAndrew Jeffery * @param ctx The msgbuf context object 57366c7723aSAndrew Jeffery * @param dst The pointer to the object into which the field at the msgbuf 57466c7723aSAndrew Jeffery * cursor should be extracted 57566c7723aSAndrew Jeffery * 57666c7723aSAndrew Jeffery * @return PLDM_SUCCESS on success, otherwise another value on error 57766c7723aSAndrew Jeffery */ 57866c7723aSAndrew Jeffery #define pldm_msgbuf_extract_p(ctx, dst) \ 57966c7723aSAndrew Jeffery _Generic((dst), \ 58066c7723aSAndrew Jeffery uint8_t *: pldm__msgbuf_extract_uint8, \ 58166c7723aSAndrew Jeffery int8_t *: pldm__msgbuf_extract_int8, \ 58266c7723aSAndrew Jeffery uint16_t *: pldm__msgbuf_extract_uint16, \ 58366c7723aSAndrew Jeffery int16_t *: pldm__msgbuf_extract_int16, \ 58466c7723aSAndrew Jeffery uint32_t *: pldm__msgbuf_extract_uint32, \ 58566c7723aSAndrew Jeffery int32_t *: pldm__msgbuf_extract_int32, \ 58666c7723aSAndrew Jeffery real32_t *: pldm__msgbuf_extract_real32)(ctx, dst) 587c63f63a2SAndrew Jeffery 588369b121aSAndrew Jeffery static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, 589369b121aSAndrew Jeffery uint8_t *dst, size_t count) 590369b121aSAndrew Jeffery { 591369b121aSAndrew Jeffery if (!ctx || !ctx->cursor || !dst) { 592369b121aSAndrew Jeffery return PLDM_ERROR_INVALID_DATA; 593369b121aSAndrew Jeffery } 594369b121aSAndrew Jeffery 595369b121aSAndrew Jeffery if (!count) { 596369b121aSAndrew Jeffery return PLDM_SUCCESS; 597369b121aSAndrew Jeffery } 598369b121aSAndrew Jeffery 599*2ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX 600*2ff8cf89SAndrew Jeffery if (count > INTMAX_MAX) { 601369b121aSAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 602369b121aSAndrew Jeffery } 603*2ff8cf89SAndrew Jeffery #endif 604369b121aSAndrew Jeffery 605*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)count) { 606*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 607*2ff8cf89SAndrew Jeffery } 608*2ff8cf89SAndrew Jeffery ctx->remaining -= (intmax_t)count; 609369b121aSAndrew Jeffery assert(ctx->remaining >= 0); 610369b121aSAndrew Jeffery if (ctx->remaining < 0) { 611369b121aSAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 612369b121aSAndrew Jeffery } 613369b121aSAndrew Jeffery 614a065eccbSAndrew Jeffery memcpy(dst, ctx->cursor, count); 615a065eccbSAndrew Jeffery ctx->cursor += count; 616369b121aSAndrew Jeffery 617369b121aSAndrew Jeffery return PLDM_SUCCESS; 618369b121aSAndrew Jeffery } 619369b121aSAndrew Jeffery 620369b121aSAndrew Jeffery #define pldm_msgbuf_extract_array(ctx, dst, count) \ 62137dd6a3dSAndrew Jeffery _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \ 62237dd6a3dSAndrew Jeffery count) 623369b121aSAndrew Jeffery 624062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, 625062c8762SThu Nguyen const uint32_t src) 626062c8762SThu Nguyen { 627062c8762SThu Nguyen uint32_t val = htole32(src); 628062c8762SThu Nguyen 629062c8762SThu Nguyen if (!ctx || !ctx->cursor) { 630062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 631062c8762SThu Nguyen } 632062c8762SThu Nguyen 633*2ff8cf89SAndrew Jeffery static_assert( 634*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 635*2ff8cf89SAndrew Jeffery sizeof(src) < INTMAX_MAX, 636*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 637*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 638*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 639*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 640*2ff8cf89SAndrew Jeffery } 641062c8762SThu Nguyen ctx->remaining -= sizeof(src); 642062c8762SThu Nguyen assert(ctx->remaining >= 0); 643062c8762SThu Nguyen if (ctx->remaining < 0) { 644062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 645062c8762SThu Nguyen } 646062c8762SThu Nguyen 647062c8762SThu Nguyen memcpy(ctx->cursor, &val, sizeof(val)); 648062c8762SThu Nguyen ctx->cursor += sizeof(src); 649062c8762SThu Nguyen 650062c8762SThu Nguyen return PLDM_SUCCESS; 651062c8762SThu Nguyen } 652062c8762SThu Nguyen 653062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, 654062c8762SThu Nguyen const uint16_t src) 655062c8762SThu Nguyen { 656062c8762SThu Nguyen uint16_t val = htole16(src); 657062c8762SThu Nguyen 658062c8762SThu Nguyen if (!ctx || !ctx->cursor) { 659062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 660062c8762SThu Nguyen } 661062c8762SThu Nguyen 662*2ff8cf89SAndrew Jeffery static_assert( 663*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 664*2ff8cf89SAndrew Jeffery sizeof(src) < INTMAX_MAX, 665*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 666*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 667*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 668*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 669*2ff8cf89SAndrew Jeffery } 670062c8762SThu Nguyen ctx->remaining -= sizeof(src); 671062c8762SThu Nguyen assert(ctx->remaining >= 0); 672062c8762SThu Nguyen if (ctx->remaining < 0) { 673062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 674062c8762SThu Nguyen } 675062c8762SThu Nguyen 676062c8762SThu Nguyen memcpy(ctx->cursor, &val, sizeof(val)); 677062c8762SThu Nguyen ctx->cursor += sizeof(src); 678062c8762SThu Nguyen 679062c8762SThu Nguyen return PLDM_SUCCESS; 680062c8762SThu Nguyen } 681062c8762SThu Nguyen 682062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, 683062c8762SThu Nguyen const uint8_t src) 684062c8762SThu Nguyen { 685062c8762SThu Nguyen if (!ctx || !ctx->cursor) { 686062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 687062c8762SThu Nguyen } 688062c8762SThu Nguyen 689*2ff8cf89SAndrew Jeffery static_assert( 690*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 691*2ff8cf89SAndrew Jeffery sizeof(src) < INTMAX_MAX, 692*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 693*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 694*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 695*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 696*2ff8cf89SAndrew Jeffery } 697062c8762SThu Nguyen ctx->remaining -= sizeof(src); 698062c8762SThu Nguyen assert(ctx->remaining >= 0); 699062c8762SThu Nguyen if (ctx->remaining < 0) { 700062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 701062c8762SThu Nguyen } 702062c8762SThu Nguyen 703062c8762SThu Nguyen memcpy(ctx->cursor, &src, sizeof(src)); 704062c8762SThu Nguyen ctx->cursor += sizeof(src); 705062c8762SThu Nguyen 706062c8762SThu Nguyen return PLDM_SUCCESS; 707062c8762SThu Nguyen } 708062c8762SThu Nguyen 709062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, 710062c8762SThu Nguyen const int32_t src) 711062c8762SThu Nguyen { 712062c8762SThu Nguyen int32_t val = htole32(src); 713062c8762SThu Nguyen 714062c8762SThu Nguyen if (!ctx || !ctx->cursor) { 715062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 716062c8762SThu Nguyen } 717062c8762SThu Nguyen 718*2ff8cf89SAndrew Jeffery static_assert( 719*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 720*2ff8cf89SAndrew Jeffery sizeof(src) < INTMAX_MAX, 721*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 722*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 723*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 724*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 725*2ff8cf89SAndrew Jeffery } 726062c8762SThu Nguyen ctx->remaining -= sizeof(src); 727062c8762SThu Nguyen assert(ctx->remaining >= 0); 728062c8762SThu Nguyen if (ctx->remaining < 0) { 729062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 730062c8762SThu Nguyen } 731062c8762SThu Nguyen 732062c8762SThu Nguyen memcpy(ctx->cursor, &val, sizeof(val)); 733062c8762SThu Nguyen ctx->cursor += sizeof(src); 734062c8762SThu Nguyen 735062c8762SThu Nguyen return PLDM_SUCCESS; 736062c8762SThu Nguyen } 737062c8762SThu Nguyen 738062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, 739062c8762SThu Nguyen const int16_t src) 740062c8762SThu Nguyen { 741062c8762SThu Nguyen int16_t val = htole16(src); 742062c8762SThu Nguyen 743062c8762SThu Nguyen if (!ctx || !ctx->cursor) { 744062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 745062c8762SThu Nguyen } 746062c8762SThu Nguyen 747*2ff8cf89SAndrew Jeffery static_assert( 748*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 749*2ff8cf89SAndrew Jeffery sizeof(src) < INTMAX_MAX, 750*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 751*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 752*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 753*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 754*2ff8cf89SAndrew Jeffery } 755062c8762SThu Nguyen ctx->remaining -= sizeof(src); 756062c8762SThu Nguyen assert(ctx->remaining >= 0); 757062c8762SThu Nguyen if (ctx->remaining < 0) { 758062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 759062c8762SThu Nguyen } 760062c8762SThu Nguyen 761062c8762SThu Nguyen memcpy(ctx->cursor, &val, sizeof(val)); 762062c8762SThu Nguyen ctx->cursor += sizeof(src); 763062c8762SThu Nguyen 764062c8762SThu Nguyen return PLDM_SUCCESS; 765062c8762SThu Nguyen } 766062c8762SThu Nguyen 767062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, 768062c8762SThu Nguyen const int8_t src) 769062c8762SThu Nguyen { 770062c8762SThu Nguyen if (!ctx || !ctx->cursor) { 771062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 772062c8762SThu Nguyen } 773062c8762SThu Nguyen 774*2ff8cf89SAndrew Jeffery static_assert( 775*2ff8cf89SAndrew Jeffery // NOLINTNEXTLINE(bugprone-sizeof-expression) 776*2ff8cf89SAndrew Jeffery sizeof(src) < INTMAX_MAX, 777*2ff8cf89SAndrew Jeffery "The following addition may not uphold the runtime assertion"); 778*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) { 779*2ff8cf89SAndrew Jeffery assert(ctx->remaining < 0); 780*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 781*2ff8cf89SAndrew Jeffery } 782062c8762SThu Nguyen ctx->remaining -= sizeof(src); 783062c8762SThu Nguyen assert(ctx->remaining >= 0); 784062c8762SThu Nguyen if (ctx->remaining < 0) { 785062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 786062c8762SThu Nguyen } 787062c8762SThu Nguyen 788062c8762SThu Nguyen memcpy(ctx->cursor, &src, sizeof(src)); 789062c8762SThu Nguyen ctx->cursor += sizeof(src); 790062c8762SThu Nguyen 791062c8762SThu Nguyen return PLDM_SUCCESS; 792062c8762SThu Nguyen } 793062c8762SThu Nguyen 794062c8762SThu Nguyen #define pldm_msgbuf_insert(dst, src) \ 79537dd6a3dSAndrew Jeffery _Generic((src), \ 79637dd6a3dSAndrew Jeffery uint8_t: pldm_msgbuf_insert_uint8, \ 79737dd6a3dSAndrew Jeffery int8_t: pldm_msgbuf_insert_int8, \ 79837dd6a3dSAndrew Jeffery uint16_t: pldm_msgbuf_insert_uint16, \ 79937dd6a3dSAndrew Jeffery int16_t: pldm_msgbuf_insert_int16, \ 80037dd6a3dSAndrew Jeffery uint32_t: pldm_msgbuf_insert_uint32, \ 80137dd6a3dSAndrew Jeffery int32_t: pldm_msgbuf_insert_int32)(dst, src) 802062c8762SThu Nguyen 803062c8762SThu Nguyen static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, 804062c8762SThu Nguyen const uint8_t *src, 805062c8762SThu Nguyen size_t count) 806062c8762SThu Nguyen { 807062c8762SThu Nguyen if (!ctx || !ctx->cursor || !src) { 808062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 809062c8762SThu Nguyen } 810062c8762SThu Nguyen 811062c8762SThu Nguyen if (!count) { 812062c8762SThu Nguyen return PLDM_SUCCESS; 813062c8762SThu Nguyen } 814062c8762SThu Nguyen 815*2ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX 816*2ff8cf89SAndrew Jeffery if (count > INTMAX_MAX) { 817062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 818062c8762SThu Nguyen } 819*2ff8cf89SAndrew Jeffery #endif 820062c8762SThu Nguyen 821*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)count) { 822*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 823*2ff8cf89SAndrew Jeffery } 824*2ff8cf89SAndrew Jeffery ctx->remaining -= (intmax_t)count; 825062c8762SThu Nguyen assert(ctx->remaining >= 0); 826062c8762SThu Nguyen if (ctx->remaining < 0) { 827062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 828062c8762SThu Nguyen } 829062c8762SThu Nguyen 830a065eccbSAndrew Jeffery memcpy(ctx->cursor, src, count); 831a065eccbSAndrew Jeffery ctx->cursor += count; 832062c8762SThu Nguyen 833062c8762SThu Nguyen return PLDM_SUCCESS; 834062c8762SThu Nguyen } 835062c8762SThu Nguyen 836062c8762SThu Nguyen #define pldm_msgbuf_insert_array(dst, src, count) \ 83737dd6a3dSAndrew Jeffery _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \ 83837dd6a3dSAndrew Jeffery count) 839062c8762SThu Nguyen 840062c8762SThu Nguyen static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, 841062c8762SThu Nguyen size_t required, void **cursor) 842062c8762SThu Nguyen { 843062c8762SThu Nguyen if (!ctx || !ctx->cursor || !cursor || *cursor) { 844062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 845062c8762SThu Nguyen } 846062c8762SThu Nguyen 847*2ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX 848*2ff8cf89SAndrew Jeffery if (required > INTMAX_MAX) { 849062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 850062c8762SThu Nguyen } 851*2ff8cf89SAndrew Jeffery #endif 852062c8762SThu Nguyen 853*2ff8cf89SAndrew Jeffery if (ctx->remaining < INTMAX_MIN + (intmax_t)required) { 854*2ff8cf89SAndrew Jeffery return PLDM_ERROR_INVALID_LENGTH; 855*2ff8cf89SAndrew Jeffery } 856*2ff8cf89SAndrew Jeffery ctx->remaining -= (intmax_t)required; 857062c8762SThu Nguyen assert(ctx->remaining >= 0); 858062c8762SThu Nguyen if (ctx->remaining < 0) { 859062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 860062c8762SThu Nguyen } 861062c8762SThu Nguyen 862062c8762SThu Nguyen *cursor = ctx->cursor; 863062c8762SThu Nguyen ctx->cursor += required; 864062c8762SThu Nguyen 865062c8762SThu Nguyen return PLDM_SUCCESS; 866062c8762SThu Nguyen } 867062c8762SThu Nguyen 868062c8762SThu Nguyen static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, 869062c8762SThu Nguyen void **cursor, size_t *len) 870062c8762SThu Nguyen { 871062c8762SThu Nguyen if (!ctx || !ctx->cursor || !cursor || *cursor || !len) { 872062c8762SThu Nguyen return PLDM_ERROR_INVALID_DATA; 873062c8762SThu Nguyen } 874062c8762SThu Nguyen 875062c8762SThu Nguyen assert(ctx->remaining >= 0); 876062c8762SThu Nguyen if (ctx->remaining < 0) { 877062c8762SThu Nguyen return PLDM_ERROR_INVALID_LENGTH; 878062c8762SThu Nguyen } 879062c8762SThu Nguyen 880062c8762SThu Nguyen *cursor = ctx->cursor; 881062c8762SThu Nguyen ctx->cursor += ctx->remaining; 882062c8762SThu Nguyen *len = ctx->remaining; 883062c8762SThu Nguyen ctx->remaining = 0; 884062c8762SThu Nguyen 885062c8762SThu Nguyen return PLDM_SUCCESS; 886062c8762SThu Nguyen } 887c63f63a2SAndrew Jeffery #ifdef __cplusplus 888c63f63a2SAndrew Jeffery } 889c63f63a2SAndrew Jeffery #endif 890c63f63a2SAndrew Jeffery 89166c7723aSAndrew Jeffery #ifdef __cplusplus 89266c7723aSAndrew Jeffery #include <type_traits> 89366c7723aSAndrew Jeffery 89466c7723aSAndrew Jeffery template <typename T> 89566c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx, 89666c7723aSAndrew Jeffery void *buf) 89766c7723aSAndrew Jeffery { 89866c7723aSAndrew Jeffery static_assert(std::is_same<uint8_t *, T>::value); 89966c7723aSAndrew Jeffery return pldm__msgbuf_extract_uint8(ctx, buf); 90066c7723aSAndrew Jeffery } 90166c7723aSAndrew Jeffery 90266c7723aSAndrew Jeffery template <typename T> 90366c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx, 90466c7723aSAndrew Jeffery void *buf) 90566c7723aSAndrew Jeffery { 90666c7723aSAndrew Jeffery static_assert(std::is_same<int8_t *, T>::value); 90766c7723aSAndrew Jeffery return pldm__msgbuf_extract_int8(ctx, buf); 90866c7723aSAndrew Jeffery } 90966c7723aSAndrew Jeffery 91066c7723aSAndrew Jeffery template <typename T> 91166c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx, 91266c7723aSAndrew Jeffery void *buf) 91366c7723aSAndrew Jeffery { 91466c7723aSAndrew Jeffery static_assert(std::is_same<uint16_t *, T>::value); 91566c7723aSAndrew Jeffery return pldm__msgbuf_extract_uint16(ctx, buf); 91666c7723aSAndrew Jeffery } 91766c7723aSAndrew Jeffery 91866c7723aSAndrew Jeffery template <typename T> 91966c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx, 92066c7723aSAndrew Jeffery void *buf) 92166c7723aSAndrew Jeffery { 92266c7723aSAndrew Jeffery static_assert(std::is_same<int16_t *, T>::value); 92366c7723aSAndrew Jeffery return pldm__msgbuf_extract_int16(ctx, buf); 92466c7723aSAndrew Jeffery } 92566c7723aSAndrew Jeffery 92666c7723aSAndrew Jeffery template <typename T> 92766c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx, 92866c7723aSAndrew Jeffery void *buf) 92966c7723aSAndrew Jeffery { 93066c7723aSAndrew Jeffery static_assert(std::is_same<uint32_t *, T>::value); 93166c7723aSAndrew Jeffery return pldm__msgbuf_extract_uint32(ctx, buf); 93266c7723aSAndrew Jeffery } 93366c7723aSAndrew Jeffery 93466c7723aSAndrew Jeffery template <typename T> 93566c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx, 93666c7723aSAndrew Jeffery void *buf) 93766c7723aSAndrew Jeffery { 93866c7723aSAndrew Jeffery static_assert(std::is_same<int32_t *, T>::value); 93966c7723aSAndrew Jeffery return pldm__msgbuf_extract_int32(ctx, buf); 94066c7723aSAndrew Jeffery } 94166c7723aSAndrew Jeffery 94266c7723aSAndrew Jeffery template <typename T> 94366c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx, 94466c7723aSAndrew Jeffery void *buf) 94566c7723aSAndrew Jeffery { 94666c7723aSAndrew Jeffery static_assert(std::is_same<real32_t *, T>::value); 94766c7723aSAndrew Jeffery return pldm__msgbuf_extract_real32(ctx, buf); 94866c7723aSAndrew Jeffery } 94966c7723aSAndrew Jeffery #endif 95066c7723aSAndrew Jeffery 951c63f63a2SAndrew Jeffery #endif /* BUF_H */ 952