xref: /openbmc/libpldm/src/msgbuf.h (revision 07febdbb)
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 
55c63f63a2SAndrew Jeffery struct pldm_msgbuf {
56062c8762SThu Nguyen 	uint8_t *cursor;
57c63f63a2SAndrew Jeffery 	ssize_t remaining;
58c63f63a2SAndrew Jeffery };
59c63f63a2SAndrew Jeffery 
60c63f63a2SAndrew Jeffery /**
61c63f63a2SAndrew Jeffery  * @brief Initialize pldm buf struct for buf extractor
62c63f63a2SAndrew Jeffery  *
63c63f63a2SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
64c63f63a2SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
65c63f63a2SAndrew Jeffery  * @param[in] buf - buffer to be extracted
66c63f63a2SAndrew Jeffery  * @param[in] len - size of buffer
67c63f63a2SAndrew Jeffery  *
68c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
69c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
70c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
71c63f63a2SAndrew Jeffery  */
72*07febdbbSAndrew Jeffery static inline int pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize,
73*07febdbbSAndrew Jeffery 				   const void *buf, size_t len)
74c63f63a2SAndrew Jeffery {
75c63f63a2SAndrew Jeffery 	if (!ctx || !buf) {
76c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
77c63f63a2SAndrew Jeffery 	}
78c63f63a2SAndrew Jeffery 
79c63f63a2SAndrew Jeffery 	if ((minsize > len) || (len > SSIZE_MAX)) {
80c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
81c63f63a2SAndrew Jeffery 	}
82c63f63a2SAndrew Jeffery 
83*07febdbbSAndrew Jeffery 	if ((uintptr_t)buf + len < len) {
84c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
85c63f63a2SAndrew Jeffery 	}
86c63f63a2SAndrew Jeffery 
87c63f63a2SAndrew Jeffery 	ctx->cursor = (uint8_t *)buf;
88c63f63a2SAndrew Jeffery 	ctx->remaining = (ssize_t)len;
89c63f63a2SAndrew Jeffery 
90c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
91c63f63a2SAndrew Jeffery }
92c63f63a2SAndrew Jeffery 
93c63f63a2SAndrew Jeffery /**
94c63f63a2SAndrew Jeffery  * @brief Validate buffer overflow state
95c63f63a2SAndrew Jeffery  *
96c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
97c63f63a2SAndrew Jeffery  *
98c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
99c63f63a2SAndrew Jeffery  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
100c63f63a2SAndrew Jeffery  * prior accesses would have occurred beyond the bounds of the buffer, and
101c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
102c63f63a2SAndrew Jeffery  * pointer.
103c63f63a2SAndrew Jeffery  */
104c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
105c63f63a2SAndrew Jeffery {
106c63f63a2SAndrew Jeffery 	if (!ctx) {
107c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
108c63f63a2SAndrew Jeffery 	}
109c63f63a2SAndrew Jeffery 
110c63f63a2SAndrew Jeffery 	return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
111c63f63a2SAndrew Jeffery }
112c63f63a2SAndrew Jeffery 
113c63f63a2SAndrew Jeffery /**
114db7b8324SAndrew Jeffery  * @brief Test whether a message buffer has been exactly consumed
115db7b8324SAndrew Jeffery  *
116db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
117db7b8324SAndrew Jeffery  *
118db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
119db7b8324SAndrew Jeffery  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
120db7b8324SAndrew Jeffery  * indicates that an incorrect sequence of accesses have occurred, and
121db7b8324SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
122db7b8324SAndrew Jeffery  * pointer.
123db7b8324SAndrew Jeffery  */
124db7b8324SAndrew Jeffery static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
125db7b8324SAndrew Jeffery {
126db7b8324SAndrew Jeffery 	if (!ctx) {
127db7b8324SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
128db7b8324SAndrew Jeffery 	}
129db7b8324SAndrew Jeffery 
130db7b8324SAndrew Jeffery 	return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
131db7b8324SAndrew Jeffery }
132db7b8324SAndrew Jeffery 
133db7b8324SAndrew Jeffery /**
134c63f63a2SAndrew Jeffery  * @brief Destroy the pldm buf
135c63f63a2SAndrew Jeffery  *
136c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
137c63f63a2SAndrew Jeffery  *
138c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
139c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
140c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
141c63f63a2SAndrew Jeffery  * bounds of the buffer.
142c63f63a2SAndrew Jeffery  */
143c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
144c63f63a2SAndrew Jeffery {
145c63f63a2SAndrew Jeffery 	int valid;
146c63f63a2SAndrew Jeffery 
147c63f63a2SAndrew Jeffery 	if (!ctx) {
148c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
149c63f63a2SAndrew Jeffery 	}
150c63f63a2SAndrew Jeffery 
151c63f63a2SAndrew Jeffery 	valid = pldm_msgbuf_validate(ctx);
152c63f63a2SAndrew Jeffery 
153c63f63a2SAndrew Jeffery 	ctx->cursor = NULL;
154c63f63a2SAndrew Jeffery 	ctx->remaining = 0;
155c63f63a2SAndrew Jeffery 
156c63f63a2SAndrew Jeffery 	return valid;
157c63f63a2SAndrew Jeffery }
158c63f63a2SAndrew Jeffery 
159c63f63a2SAndrew Jeffery /**
160db7b8324SAndrew Jeffery  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
161db7b8324SAndrew Jeffery  * has been completely consumed without overflow
162db7b8324SAndrew Jeffery  *
163db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context
164db7b8324SAndrew Jeffery  *
165db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
166db7b8324SAndrew Jeffery  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
167db7b8324SAndrew Jeffery  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
168db7b8324SAndrew Jeffery  * have occurred byond the bounds of the buffer
169db7b8324SAndrew Jeffery  */
170db7b8324SAndrew Jeffery static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
171db7b8324SAndrew Jeffery {
172db7b8324SAndrew Jeffery 	int consumed;
173db7b8324SAndrew Jeffery 
174db7b8324SAndrew Jeffery 	if (!ctx) {
175db7b8324SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
176db7b8324SAndrew Jeffery 	}
177db7b8324SAndrew Jeffery 
178db7b8324SAndrew Jeffery 	consumed = pldm_msgbuf_consumed(ctx);
179db7b8324SAndrew Jeffery 
180db7b8324SAndrew Jeffery 	ctx->cursor = NULL;
181db7b8324SAndrew Jeffery 	ctx->remaining = 0;
182db7b8324SAndrew Jeffery 
183db7b8324SAndrew Jeffery 	return consumed;
184db7b8324SAndrew Jeffery }
185db7b8324SAndrew Jeffery 
18666c7723aSAndrew Jeffery /*
18766c7723aSAndrew Jeffery  * Exploit the pre-processor to perform type checking by macro substitution.
18866c7723aSAndrew Jeffery  *
18966c7723aSAndrew Jeffery  * A C type is defined by its alignment as well as its object
19066c7723aSAndrew Jeffery  * size, and compilers have a hammer to enforce it in the form of
19166c7723aSAndrew Jeffery  * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
19266c7723aSAndrew Jeffery  * the libpldm public API this presents a problem: Naively attempting to use the
19366c7723aSAndrew Jeffery  * msgbuf APIs on a member of a packed struct would yield an error.
19466c7723aSAndrew Jeffery  *
19566c7723aSAndrew Jeffery  * The msgbuf APIs are implemented such that data is moved through unaligned
19666c7723aSAndrew Jeffery  * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
19766c7723aSAndrew Jeffery  * make the object pointers take a trip through `void *` at its API boundary.
19866c7723aSAndrew Jeffery  * That presents a bit too much of an opportunity to non-surgically remove your
19966c7723aSAndrew Jeffery  * own foot, so here we set about doing something to mitigate that as well.
20066c7723aSAndrew Jeffery  *
20166c7723aSAndrew Jeffery  * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
20266c7723aSAndrew Jeffery  * only for the purpose of object sizes, disregarding alignment. We have a few
20366c7723aSAndrew Jeffery  * constraints that cause some headaches:
20466c7723aSAndrew Jeffery  *
20566c7723aSAndrew Jeffery  * 1. We have to perform the type-check before a call through a C function,
20666c7723aSAndrew Jeffery  *    as the function must take the object pointer argument as `void *`.
20766c7723aSAndrew Jeffery  *    Essentially, this constrains us to doing something with macros.
20866c7723aSAndrew Jeffery  *
20966c7723aSAndrew Jeffery  * 2. While libpldm is a C library, its test suite is written in C++ to take
21066c7723aSAndrew Jeffery  *    advantage of gtest.
21166c7723aSAndrew Jeffery  *
21266c7723aSAndrew Jeffery  * 3. Ideally we'd do something with C's `static_assert()`, however
21366c7723aSAndrew Jeffery  *    `static_assert()` is defined as void, and as we're constrained to macros,
21466c7723aSAndrew Jeffery  *    using `static_assert()` would require a statement-expression
21566c7723aSAndrew Jeffery  *
21666c7723aSAndrew Jeffery  * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
21766c7723aSAndrew Jeffery  *    are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
21866c7723aSAndrew Jeffery  *    the purpose of enabling statement-expressions in this one instance.
21966c7723aSAndrew Jeffery  *
22066c7723aSAndrew Jeffery  * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
22166c7723aSAndrew Jeffery  *    however it's implemented in terms of `_Generic()`, which is not available
22266c7723aSAndrew Jeffery  *    in C++.
22366c7723aSAndrew Jeffery  *
22466c7723aSAndrew Jeffery  * Combined this means we need separate solutions for C and C++.
22566c7723aSAndrew Jeffery  *
22666c7723aSAndrew Jeffery  * For C, as we don't have statement-expressions, we need to exploit some other
22766c7723aSAndrew Jeffery  * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
22866c7723aSAndrew Jeffery  * API function call. We also have to take care of the fact that the call-sites
22966c7723aSAndrew Jeffery  * may be in the context of a variable assignment for error-handling purposes.
23066c7723aSAndrew Jeffery  * The key observation is that we can use the comma operator as a sequence point
23166c7723aSAndrew Jeffery  * to order the type check before the API call, discarding the "result" value of
23266c7723aSAndrew Jeffery  * the type check and yielding the return value of the API call.
23366c7723aSAndrew Jeffery  *
23466c7723aSAndrew Jeffery  * C++ could be less of a headache than the C as we can leverage template
23566c7723aSAndrew Jeffery  * functions. An advantage of template functions is that while their definition
23666c7723aSAndrew Jeffery  * is driven by instantion, the definition does not appear at the source
23766c7723aSAndrew Jeffery  * location of the instantation, which gives it a great leg-up over the problems
23866c7723aSAndrew Jeffery  * we have in the C path. However, the use of the msgbuf APIs in the test suite
23966c7723aSAndrew Jeffery  * still makes things somewhat tricky, as the call-sites in the test suite are
24066c7723aSAndrew Jeffery  * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
24166c7723aSAndrew Jeffery  * takes both the object type and the required type as template arguments, and
24266c7723aSAndrew Jeffery  * then define the object pointer parameter as `void *` for a call through to
24366c7723aSAndrew Jeffery  * the appropriate msgbuf API. However, because the msgbuf API call-sites are
24466c7723aSAndrew Jeffery  * encapsulated in gtest macros, use of commas in the template specification
24566c7723aSAndrew Jeffery  * causes pre-processor confusion. In this way we're constrained to only one
24666c7723aSAndrew Jeffery  * template argument per function.
24766c7723aSAndrew Jeffery  *
24866c7723aSAndrew Jeffery  * Implement the C++ path using template functions that take the destination
24966c7723aSAndrew Jeffery  * object type as a template argument, while the name of the function symbols
25066c7723aSAndrew Jeffery  * are derived from the required type. The manual implementations of these
25166c7723aSAndrew Jeffery  * appear at the end of the header. The type safety is actually enforced
25266c7723aSAndrew Jeffery  * by `static_assert()` this time, as we can use statements as we're not
25366c7723aSAndrew Jeffery  * constrained to an expression in the templated function body.
25466c7723aSAndrew Jeffery  *
25566c7723aSAndrew Jeffery  * The invocations of pldm_msgbuf_extract_typecheck() typically result in
25666c7723aSAndrew Jeffery  * double-evaluation of some arguments. We're not yet bothered by this for two
25766c7723aSAndrew Jeffery  * reasons:
25866c7723aSAndrew Jeffery  *
25966c7723aSAndrew Jeffery  * 1. The nature of the current call-sites are such that there are no
26066c7723aSAndrew Jeffery  *    argument expressions that result in undesirable side-effects
26166c7723aSAndrew Jeffery  *
26266c7723aSAndrew Jeffery  * 2. It's an API internal to the libpldm implementation, and we can fix things
26366c7723aSAndrew Jeffery  *    whenever something crops up the violates the observation in 1.
26466c7723aSAndrew Jeffery  */
26566c7723aSAndrew Jeffery #ifdef __cplusplus
26666c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
26766c7723aSAndrew Jeffery 	pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
26866c7723aSAndrew Jeffery #else
26966c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
27066c7723aSAndrew Jeffery 	(pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
27166c7723aSAndrew Jeffery #endif
27266c7723aSAndrew Jeffery 
273db7b8324SAndrew Jeffery /**
274c63f63a2SAndrew Jeffery  * @brief pldm_msgbuf extractor for a uint8_t
275c63f63a2SAndrew Jeffery  *
276c63f63a2SAndrew Jeffery  * @param[inout] ctx - pldm_msgbuf context for extractor
277c63f63a2SAndrew Jeffery  * @param[out] dst - destination of extracted value
278c63f63a2SAndrew Jeffery  *
279c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
280c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH otherwise.
281c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
282c63f63a2SAndrew Jeffery  */
28366c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint8(ctx, dst)                                    \
28466c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8,     \
28566c7723aSAndrew Jeffery 				      dst, ctx, dst)
28666c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
28766c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
288c63f63a2SAndrew Jeffery {
289c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
290c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
291c63f63a2SAndrew Jeffery 	}
292c63f63a2SAndrew Jeffery 
29366c7723aSAndrew Jeffery 	ctx->remaining -= sizeof(uint8_t);
294c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
295c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
296c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
297c63f63a2SAndrew Jeffery 	}
298c63f63a2SAndrew Jeffery 
29966c7723aSAndrew Jeffery 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
30066c7723aSAndrew Jeffery 
301c63f63a2SAndrew Jeffery 	ctx->cursor++;
302c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
303c63f63a2SAndrew Jeffery }
304c63f63a2SAndrew Jeffery 
30566c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
30666c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
30766c7723aSAndrew Jeffery 				      ctx, dst)
30866c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
30966c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
310c63f63a2SAndrew Jeffery {
311c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
312c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
313c63f63a2SAndrew Jeffery 	}
314c63f63a2SAndrew Jeffery 
31566c7723aSAndrew Jeffery 	ctx->remaining -= sizeof(int8_t);
316c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
317c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
318c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
319c63f63a2SAndrew Jeffery 	}
320c63f63a2SAndrew Jeffery 
32166c7723aSAndrew Jeffery 	memcpy(dst, ctx->cursor, sizeof(int8_t));
322c63f63a2SAndrew Jeffery 	ctx->cursor++;
323c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
324c63f63a2SAndrew Jeffery }
325c63f63a2SAndrew Jeffery 
32666c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
32766c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
32866c7723aSAndrew Jeffery 				      dst, ctx, dst)
32966c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
33066c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
33166c7723aSAndrew Jeffery 					      void *dst)
332c63f63a2SAndrew Jeffery {
333c63f63a2SAndrew Jeffery 	uint16_t ldst;
334c63f63a2SAndrew Jeffery 
335c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
336c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
337c63f63a2SAndrew Jeffery 	}
338c63f63a2SAndrew Jeffery 
339c63f63a2SAndrew Jeffery 	// Check for buffer overflow. If we overflow, account for the request as
340c63f63a2SAndrew Jeffery 	// negative values in ctx->remaining. This way we can debug how far
341c63f63a2SAndrew Jeffery 	// we've overflowed.
342c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
343c63f63a2SAndrew Jeffery 
344c63f63a2SAndrew Jeffery 	// Prevent the access if it would overflow. First, assert so we blow up
345c63f63a2SAndrew Jeffery 	// the test suite right at the point of failure. However, cater to
346c63f63a2SAndrew Jeffery 	// -DNDEBUG by explicitly testing that the access is valid.
347c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
348c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
349c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
350c63f63a2SAndrew Jeffery 	}
351c63f63a2SAndrew Jeffery 
352c63f63a2SAndrew Jeffery 	// Use memcpy() to have the compiler deal with any alignment
353c63f63a2SAndrew Jeffery 	// issues on the target architecture
354c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
355c63f63a2SAndrew Jeffery 
356c63f63a2SAndrew Jeffery 	// Only assign the target value once it's correctly decoded
35766c7723aSAndrew Jeffery 	ldst = le16toh(ldst);
35866c7723aSAndrew Jeffery 
35966c7723aSAndrew Jeffery 	// Allow storing to unaligned
36066c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
36166c7723aSAndrew Jeffery 
362c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
363c63f63a2SAndrew Jeffery 
364c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
365c63f63a2SAndrew Jeffery }
366c63f63a2SAndrew Jeffery 
36766c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
36866c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
36966c7723aSAndrew Jeffery 				      dst, ctx, dst)
37066c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
37166c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
372c63f63a2SAndrew Jeffery {
373c63f63a2SAndrew Jeffery 	int16_t ldst;
374c63f63a2SAndrew Jeffery 
375c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
376c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
377c63f63a2SAndrew Jeffery 	}
378c63f63a2SAndrew Jeffery 
379c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
380c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
381c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
382c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
383c63f63a2SAndrew Jeffery 	}
384c63f63a2SAndrew Jeffery 
385c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
386c63f63a2SAndrew Jeffery 
38766c7723aSAndrew Jeffery 	ldst = le16toh(ldst);
38866c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
389c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
390c63f63a2SAndrew Jeffery 
391c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
392c63f63a2SAndrew Jeffery }
393c63f63a2SAndrew Jeffery 
39466c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
39566c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
39666c7723aSAndrew Jeffery 				      dst, ctx, dst)
39766c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
39866c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
39966c7723aSAndrew Jeffery 					      void *dst)
400c63f63a2SAndrew Jeffery {
401c63f63a2SAndrew Jeffery 	uint32_t ldst;
402c63f63a2SAndrew Jeffery 
403c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
404c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
405c63f63a2SAndrew Jeffery 	}
406c63f63a2SAndrew Jeffery 
407c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
408c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
409c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
410c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
411c63f63a2SAndrew Jeffery 	}
412c63f63a2SAndrew Jeffery 
413c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
414c63f63a2SAndrew Jeffery 
41566c7723aSAndrew Jeffery 	ldst = le32toh(ldst);
41666c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
417c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
418c63f63a2SAndrew Jeffery 
419c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
420c63f63a2SAndrew Jeffery }
421c63f63a2SAndrew Jeffery 
42266c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
42366c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
42466c7723aSAndrew Jeffery 				      dst, ctx, dst)
42566c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
42666c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
427c63f63a2SAndrew Jeffery {
428c63f63a2SAndrew Jeffery 	int32_t ldst;
429c63f63a2SAndrew Jeffery 
430c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
431c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
432c63f63a2SAndrew Jeffery 	}
433c63f63a2SAndrew Jeffery 
434c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
435c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
436c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
437c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
438c63f63a2SAndrew Jeffery 	}
439c63f63a2SAndrew Jeffery 
440c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
441c63f63a2SAndrew Jeffery 
44266c7723aSAndrew Jeffery 	ldst = le32toh(ldst);
44366c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
444c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
445c63f63a2SAndrew Jeffery 
446c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
447c63f63a2SAndrew Jeffery }
448c63f63a2SAndrew Jeffery 
44966c7723aSAndrew Jeffery #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
45066c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
45166c7723aSAndrew Jeffery 				      dst, ctx, dst)
45266c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
45366c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx,
45466c7723aSAndrew Jeffery 					      void *dst)
455c63f63a2SAndrew Jeffery {
456c63f63a2SAndrew Jeffery 	uint32_t ldst;
457c63f63a2SAndrew Jeffery 
45866c7723aSAndrew Jeffery 	_Static_assert(sizeof(real32_t) == sizeof(ldst),
45966c7723aSAndrew Jeffery 		       "Mismatched type sizes for dst and ldst");
46066c7723aSAndrew Jeffery 
461c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
462c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
463c63f63a2SAndrew Jeffery 	}
464c63f63a2SAndrew Jeffery 
465c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
466c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
467c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
468c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
469c63f63a2SAndrew Jeffery 	}
470c63f63a2SAndrew Jeffery 
471c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
472c63f63a2SAndrew Jeffery 	ldst = le32toh(ldst);
47366c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
47466c7723aSAndrew Jeffery 	ctx->cursor += sizeof(ldst);
475c63f63a2SAndrew Jeffery 
476c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
477c63f63a2SAndrew Jeffery }
478c63f63a2SAndrew Jeffery 
47966c7723aSAndrew Jeffery /**
48066c7723aSAndrew Jeffery  * Extract the field at the msgbuf cursor into the lvalue named by dst.
48166c7723aSAndrew Jeffery  *
48266c7723aSAndrew Jeffery  * @param ctx The msgbuf context object
48366c7723aSAndrew Jeffery  * @param dst The lvalue into which the field at the msgbuf cursor should be
48466c7723aSAndrew Jeffery  *            extracted
48566c7723aSAndrew Jeffery  *
48666c7723aSAndrew Jeffery  * @return PLDM_SUCCESS on success, otherwise another value on error
48766c7723aSAndrew Jeffery  */
488c63f63a2SAndrew Jeffery #define pldm_msgbuf_extract(ctx, dst)                                          \
48966c7723aSAndrew Jeffery 	_Generic((dst),                                                        \
49066c7723aSAndrew Jeffery 		uint8_t: pldm__msgbuf_extract_uint8,                           \
49166c7723aSAndrew Jeffery 		int8_t: pldm__msgbuf_extract_int8,                             \
49266c7723aSAndrew Jeffery 		uint16_t: pldm__msgbuf_extract_uint16,                         \
49366c7723aSAndrew Jeffery 		int16_t: pldm__msgbuf_extract_int16,                           \
49466c7723aSAndrew Jeffery 		uint32_t: pldm__msgbuf_extract_uint32,                         \
49566c7723aSAndrew Jeffery 		int32_t: pldm__msgbuf_extract_int32,                           \
49666c7723aSAndrew Jeffery 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
49766c7723aSAndrew Jeffery 
49866c7723aSAndrew Jeffery /**
49966c7723aSAndrew Jeffery  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
50066c7723aSAndrew Jeffery  *
50166c7723aSAndrew Jeffery  * @param ctx The msgbuf context object
50266c7723aSAndrew Jeffery  * @param dst The pointer to the object into which the field at the msgbuf
50366c7723aSAndrew Jeffery  *            cursor should be extracted
50466c7723aSAndrew Jeffery  *
50566c7723aSAndrew Jeffery  * @return PLDM_SUCCESS on success, otherwise another value on error
50666c7723aSAndrew Jeffery  */
50766c7723aSAndrew Jeffery #define pldm_msgbuf_extract_p(ctx, dst)                                        \
50866c7723aSAndrew Jeffery 	_Generic((dst),                                                        \
50966c7723aSAndrew Jeffery 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
51066c7723aSAndrew Jeffery 		int8_t *: pldm__msgbuf_extract_int8,                           \
51166c7723aSAndrew Jeffery 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
51266c7723aSAndrew Jeffery 		int16_t *: pldm__msgbuf_extract_int16,                         \
51366c7723aSAndrew Jeffery 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
51466c7723aSAndrew Jeffery 		int32_t *: pldm__msgbuf_extract_int32,                         \
51566c7723aSAndrew Jeffery 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
516c63f63a2SAndrew Jeffery 
517369b121aSAndrew Jeffery static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
518369b121aSAndrew Jeffery 						  uint8_t *dst, size_t count)
519369b121aSAndrew Jeffery {
520369b121aSAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
521369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
522369b121aSAndrew Jeffery 	}
523369b121aSAndrew Jeffery 
524369b121aSAndrew Jeffery 	if (!count) {
525369b121aSAndrew Jeffery 		return PLDM_SUCCESS;
526369b121aSAndrew Jeffery 	}
527369b121aSAndrew Jeffery 
528a065eccbSAndrew Jeffery 	if (count >= SSIZE_MAX) {
529369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
530369b121aSAndrew Jeffery 	}
531369b121aSAndrew Jeffery 
532a065eccbSAndrew Jeffery 	ctx->remaining -= (ssize_t)count;
533369b121aSAndrew Jeffery 	assert(ctx->remaining >= 0);
534369b121aSAndrew Jeffery 	if (ctx->remaining < 0) {
535369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
536369b121aSAndrew Jeffery 	}
537369b121aSAndrew Jeffery 
538a065eccbSAndrew Jeffery 	memcpy(dst, ctx->cursor, count);
539a065eccbSAndrew Jeffery 	ctx->cursor += count;
540369b121aSAndrew Jeffery 
541369b121aSAndrew Jeffery 	return PLDM_SUCCESS;
542369b121aSAndrew Jeffery }
543369b121aSAndrew Jeffery 
544369b121aSAndrew Jeffery #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
54537dd6a3dSAndrew Jeffery 	_Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
54637dd6a3dSAndrew Jeffery 								     count)
547369b121aSAndrew Jeffery 
548062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
549062c8762SThu Nguyen 					    const uint32_t src)
550062c8762SThu Nguyen {
551062c8762SThu Nguyen 	uint32_t val = htole32(src);
552062c8762SThu Nguyen 
553062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
554062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
555062c8762SThu Nguyen 	}
556062c8762SThu Nguyen 
557062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
558062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
559062c8762SThu Nguyen 	if (ctx->remaining < 0) {
560062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
561062c8762SThu Nguyen 	}
562062c8762SThu Nguyen 
563062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
564062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
565062c8762SThu Nguyen 
566062c8762SThu Nguyen 	return PLDM_SUCCESS;
567062c8762SThu Nguyen }
568062c8762SThu Nguyen 
569062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
570062c8762SThu Nguyen 					    const uint16_t src)
571062c8762SThu Nguyen {
572062c8762SThu Nguyen 	uint16_t val = htole16(src);
573062c8762SThu Nguyen 
574062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
575062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
576062c8762SThu Nguyen 	}
577062c8762SThu Nguyen 
578062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
579062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
580062c8762SThu Nguyen 	if (ctx->remaining < 0) {
581062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
582062c8762SThu Nguyen 	}
583062c8762SThu Nguyen 
584062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
585062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
586062c8762SThu Nguyen 
587062c8762SThu Nguyen 	return PLDM_SUCCESS;
588062c8762SThu Nguyen }
589062c8762SThu Nguyen 
590062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
591062c8762SThu Nguyen 					   const uint8_t src)
592062c8762SThu Nguyen {
593062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
594062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
595062c8762SThu Nguyen 	}
596062c8762SThu Nguyen 
597062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
598062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
599062c8762SThu Nguyen 	if (ctx->remaining < 0) {
600062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
601062c8762SThu Nguyen 	}
602062c8762SThu Nguyen 
603062c8762SThu Nguyen 	memcpy(ctx->cursor, &src, sizeof(src));
604062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
605062c8762SThu Nguyen 
606062c8762SThu Nguyen 	return PLDM_SUCCESS;
607062c8762SThu Nguyen }
608062c8762SThu Nguyen 
609062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
610062c8762SThu Nguyen 					   const int32_t src)
611062c8762SThu Nguyen {
612062c8762SThu Nguyen 	int32_t val = htole32(src);
613062c8762SThu Nguyen 
614062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
615062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
616062c8762SThu Nguyen 	}
617062c8762SThu Nguyen 
618062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
619062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
620062c8762SThu Nguyen 	if (ctx->remaining < 0) {
621062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
622062c8762SThu Nguyen 	}
623062c8762SThu Nguyen 
624062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
625062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
626062c8762SThu Nguyen 
627062c8762SThu Nguyen 	return PLDM_SUCCESS;
628062c8762SThu Nguyen }
629062c8762SThu Nguyen 
630062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
631062c8762SThu Nguyen 					   const int16_t src)
632062c8762SThu Nguyen {
633062c8762SThu Nguyen 	int16_t val = htole16(src);
634062c8762SThu Nguyen 
635062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
636062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
637062c8762SThu Nguyen 	}
638062c8762SThu Nguyen 
639062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
640062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
641062c8762SThu Nguyen 	if (ctx->remaining < 0) {
642062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
643062c8762SThu Nguyen 	}
644062c8762SThu Nguyen 
645062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
646062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
647062c8762SThu Nguyen 
648062c8762SThu Nguyen 	return PLDM_SUCCESS;
649062c8762SThu Nguyen }
650062c8762SThu Nguyen 
651062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
652062c8762SThu Nguyen 					  const int8_t src)
653062c8762SThu Nguyen {
654062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
655062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
656062c8762SThu Nguyen 	}
657062c8762SThu Nguyen 
658062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
659062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
660062c8762SThu Nguyen 	if (ctx->remaining < 0) {
661062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
662062c8762SThu Nguyen 	}
663062c8762SThu Nguyen 
664062c8762SThu Nguyen 	memcpy(ctx->cursor, &src, sizeof(src));
665062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
666062c8762SThu Nguyen 
667062c8762SThu Nguyen 	return PLDM_SUCCESS;
668062c8762SThu Nguyen }
669062c8762SThu Nguyen 
670062c8762SThu Nguyen #define pldm_msgbuf_insert(dst, src)                                           \
67137dd6a3dSAndrew Jeffery 	_Generic((src),                                                        \
67237dd6a3dSAndrew Jeffery 		uint8_t: pldm_msgbuf_insert_uint8,                             \
67337dd6a3dSAndrew Jeffery 		int8_t: pldm_msgbuf_insert_int8,                               \
67437dd6a3dSAndrew Jeffery 		uint16_t: pldm_msgbuf_insert_uint16,                           \
67537dd6a3dSAndrew Jeffery 		int16_t: pldm_msgbuf_insert_int16,                             \
67637dd6a3dSAndrew Jeffery 		uint32_t: pldm_msgbuf_insert_uint32,                           \
67737dd6a3dSAndrew Jeffery 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
678062c8762SThu Nguyen 
679062c8762SThu Nguyen static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
680062c8762SThu Nguyen 						 const uint8_t *src,
681062c8762SThu Nguyen 						 size_t count)
682062c8762SThu Nguyen {
683062c8762SThu Nguyen 	if (!ctx || !ctx->cursor || !src) {
684062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
685062c8762SThu Nguyen 	}
686062c8762SThu Nguyen 
687062c8762SThu Nguyen 	if (!count) {
688062c8762SThu Nguyen 		return PLDM_SUCCESS;
689062c8762SThu Nguyen 	}
690062c8762SThu Nguyen 
691a065eccbSAndrew Jeffery 	if (count >= SSIZE_MAX) {
692062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
693062c8762SThu Nguyen 	}
694062c8762SThu Nguyen 
695a065eccbSAndrew Jeffery 	ctx->remaining -= (ssize_t)count;
696062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
697062c8762SThu Nguyen 	if (ctx->remaining < 0) {
698062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
699062c8762SThu Nguyen 	}
700062c8762SThu Nguyen 
701a065eccbSAndrew Jeffery 	memcpy(ctx->cursor, src, count);
702a065eccbSAndrew Jeffery 	ctx->cursor += count;
703062c8762SThu Nguyen 
704062c8762SThu Nguyen 	return PLDM_SUCCESS;
705062c8762SThu Nguyen }
706062c8762SThu Nguyen 
707062c8762SThu Nguyen #define pldm_msgbuf_insert_array(dst, src, count)                              \
70837dd6a3dSAndrew Jeffery 	_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src,  \
70937dd6a3dSAndrew Jeffery 								    count)
710062c8762SThu Nguyen 
711062c8762SThu Nguyen static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
712062c8762SThu Nguyen 					    size_t required, void **cursor)
713062c8762SThu Nguyen {
714062c8762SThu Nguyen 	if (!ctx || !ctx->cursor || !cursor || *cursor) {
715062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
716062c8762SThu Nguyen 	}
717062c8762SThu Nguyen 
718062c8762SThu Nguyen 	if (required > SSIZE_MAX) {
719062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
720062c8762SThu Nguyen 	}
721062c8762SThu Nguyen 
722062c8762SThu Nguyen 	ctx->remaining -= (ssize_t)required;
723062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
724062c8762SThu Nguyen 	if (ctx->remaining < 0) {
725062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
726062c8762SThu Nguyen 	}
727062c8762SThu Nguyen 
728062c8762SThu Nguyen 	*cursor = ctx->cursor;
729062c8762SThu Nguyen 	ctx->cursor += required;
730062c8762SThu Nguyen 
731062c8762SThu Nguyen 	return PLDM_SUCCESS;
732062c8762SThu Nguyen }
733062c8762SThu Nguyen 
734062c8762SThu Nguyen static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
735062c8762SThu Nguyen 					     void **cursor, size_t *len)
736062c8762SThu Nguyen {
737062c8762SThu Nguyen 	if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
738062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
739062c8762SThu Nguyen 	}
740062c8762SThu Nguyen 
741062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
742062c8762SThu Nguyen 	if (ctx->remaining < 0) {
743062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
744062c8762SThu Nguyen 	}
745062c8762SThu Nguyen 
746062c8762SThu Nguyen 	*cursor = ctx->cursor;
747062c8762SThu Nguyen 	ctx->cursor += ctx->remaining;
748062c8762SThu Nguyen 	*len = ctx->remaining;
749062c8762SThu Nguyen 	ctx->remaining = 0;
750062c8762SThu Nguyen 
751062c8762SThu Nguyen 	return PLDM_SUCCESS;
752062c8762SThu Nguyen }
753c63f63a2SAndrew Jeffery #ifdef __cplusplus
754c63f63a2SAndrew Jeffery }
755c63f63a2SAndrew Jeffery #endif
756c63f63a2SAndrew Jeffery 
75766c7723aSAndrew Jeffery #ifdef __cplusplus
75866c7723aSAndrew Jeffery #include <type_traits>
75966c7723aSAndrew Jeffery 
76066c7723aSAndrew Jeffery template <typename T>
76166c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
76266c7723aSAndrew Jeffery 						void *buf)
76366c7723aSAndrew Jeffery {
76466c7723aSAndrew Jeffery 	static_assert(std::is_same<uint8_t *, T>::value);
76566c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint8(ctx, buf);
76666c7723aSAndrew Jeffery }
76766c7723aSAndrew Jeffery 
76866c7723aSAndrew Jeffery template <typename T>
76966c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
77066c7723aSAndrew Jeffery 					       void *buf)
77166c7723aSAndrew Jeffery {
77266c7723aSAndrew Jeffery 	static_assert(std::is_same<int8_t *, T>::value);
77366c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int8(ctx, buf);
77466c7723aSAndrew Jeffery }
77566c7723aSAndrew Jeffery 
77666c7723aSAndrew Jeffery template <typename T>
77766c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
77866c7723aSAndrew Jeffery 						 void *buf)
77966c7723aSAndrew Jeffery {
78066c7723aSAndrew Jeffery 	static_assert(std::is_same<uint16_t *, T>::value);
78166c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint16(ctx, buf);
78266c7723aSAndrew Jeffery }
78366c7723aSAndrew Jeffery 
78466c7723aSAndrew Jeffery template <typename T>
78566c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
78666c7723aSAndrew Jeffery 						void *buf)
78766c7723aSAndrew Jeffery {
78866c7723aSAndrew Jeffery 	static_assert(std::is_same<int16_t *, T>::value);
78966c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int16(ctx, buf);
79066c7723aSAndrew Jeffery }
79166c7723aSAndrew Jeffery 
79266c7723aSAndrew Jeffery template <typename T>
79366c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
79466c7723aSAndrew Jeffery 						 void *buf)
79566c7723aSAndrew Jeffery {
79666c7723aSAndrew Jeffery 	static_assert(std::is_same<uint32_t *, T>::value);
79766c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint32(ctx, buf);
79866c7723aSAndrew Jeffery }
79966c7723aSAndrew Jeffery 
80066c7723aSAndrew Jeffery template <typename T>
80166c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
80266c7723aSAndrew Jeffery 						void *buf)
80366c7723aSAndrew Jeffery {
80466c7723aSAndrew Jeffery 	static_assert(std::is_same<int32_t *, T>::value);
80566c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int32(ctx, buf);
80666c7723aSAndrew Jeffery }
80766c7723aSAndrew Jeffery 
80866c7723aSAndrew Jeffery template <typename T>
80966c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
81066c7723aSAndrew Jeffery 						 void *buf)
81166c7723aSAndrew Jeffery {
81266c7723aSAndrew Jeffery 	static_assert(std::is_same<real32_t *, T>::value);
81366c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_real32(ctx, buf);
81466c7723aSAndrew Jeffery }
81566c7723aSAndrew Jeffery #endif
81666c7723aSAndrew Jeffery 
817c63f63a2SAndrew Jeffery #endif /* BUF_H */
818