xref: /openbmc/libpldm/src/msgbuf.h (revision c8df31c1)
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>
49*c8df31c1SAndrew Jeffery #include <errno.h>
50c63f63a2SAndrew Jeffery #include <limits.h>
51c63f63a2SAndrew Jeffery #include <stdbool.h>
5266c7723aSAndrew Jeffery #include <stdint.h>
53c63f63a2SAndrew Jeffery #include <string.h>
54c63f63a2SAndrew Jeffery #include <sys/types.h>
55c63f63a2SAndrew Jeffery 
562ff8cf89SAndrew Jeffery /*
572ff8cf89SAndrew Jeffery  * We can't use static_assert() outside of some other C construct. Deal
582ff8cf89SAndrew Jeffery  * with high-level global assertions by burying them in an unused struct
592ff8cf89SAndrew Jeffery  * declaration, that has a sole member for compliance with the requirement that
602ff8cf89SAndrew Jeffery  * types must have a size.
612ff8cf89SAndrew Jeffery */
622ff8cf89SAndrew Jeffery static struct {
632ff8cf89SAndrew Jeffery 	static_assert(
642ff8cf89SAndrew Jeffery 		INTMAX_MAX != SIZE_MAX,
652ff8cf89SAndrew Jeffery 		"Extraction and insertion value comparisons may be broken");
662ff8cf89SAndrew Jeffery 	static_assert(INTMAX_MIN + INTMAX_MAX <= 0,
672ff8cf89SAndrew Jeffery 		      "Extraction and insertion arithmetic may be broken");
68*c8df31c1SAndrew Jeffery 	static_assert(PLDM_SUCCESS == 0, "Error handling is broken");
692ff8cf89SAndrew Jeffery 	int compliance;
702ff8cf89SAndrew Jeffery } build_assertions __attribute__((unused));
712ff8cf89SAndrew Jeffery 
72*c8df31c1SAndrew Jeffery enum pldm_msgbuf_error_mode {
73*c8df31c1SAndrew Jeffery 	PLDM_MSGBUF_PLDM_CC = 0x5a,
74*c8df31c1SAndrew Jeffery 	PLDM_MSGBUF_C_ERRNO = 0xa5,
75*c8df31c1SAndrew Jeffery };
76*c8df31c1SAndrew Jeffery 
77c63f63a2SAndrew Jeffery struct pldm_msgbuf {
78062c8762SThu Nguyen 	uint8_t *cursor;
792ff8cf89SAndrew Jeffery 	intmax_t remaining;
80*c8df31c1SAndrew Jeffery 	enum pldm_msgbuf_error_mode mode;
81c63f63a2SAndrew Jeffery };
82c63f63a2SAndrew Jeffery 
83*c8df31c1SAndrew Jeffery __attribute__((always_inline)) static inline int
84*c8df31c1SAndrew Jeffery pldm_msgbuf_status(struct pldm_msgbuf *ctx, unsigned int err)
85*c8df31c1SAndrew Jeffery {
86*c8df31c1SAndrew Jeffery 	int rc;
87*c8df31c1SAndrew Jeffery 
88*c8df31c1SAndrew Jeffery 	assert(err != 0);
89*c8df31c1SAndrew Jeffery 	assert(err <= INT_MAX);
90*c8df31c1SAndrew Jeffery 
91*c8df31c1SAndrew Jeffery 	if (ctx->mode == PLDM_MSGBUF_C_ERRNO) {
92*c8df31c1SAndrew Jeffery 		if (err > INT_MAX) {
93*c8df31c1SAndrew Jeffery 			return -EINVAL;
94*c8df31c1SAndrew Jeffery 		}
95*c8df31c1SAndrew Jeffery 
96*c8df31c1SAndrew Jeffery 		static_assert(INT_MIN + INT_MAX < 0,
97*c8df31c1SAndrew Jeffery 			      "Arithmetic assumption failure");
98*c8df31c1SAndrew Jeffery 		return -((int)err);
99*c8df31c1SAndrew Jeffery 	}
100*c8df31c1SAndrew Jeffery 
101*c8df31c1SAndrew Jeffery 	if (err > INT_MAX) {
102*c8df31c1SAndrew Jeffery 		return PLDM_ERROR;
103*c8df31c1SAndrew Jeffery 	}
104*c8df31c1SAndrew Jeffery 
105*c8df31c1SAndrew Jeffery 	assert(ctx->mode == PLDM_MSGBUF_PLDM_CC);
106*c8df31c1SAndrew Jeffery 	switch (err) {
107*c8df31c1SAndrew Jeffery 	case EINVAL:
108*c8df31c1SAndrew Jeffery 		rc = PLDM_ERROR_INVALID_DATA;
109*c8df31c1SAndrew Jeffery 		break;
110*c8df31c1SAndrew Jeffery 	case EBADMSG:
111*c8df31c1SAndrew Jeffery 	case EOVERFLOW:
112*c8df31c1SAndrew Jeffery 		rc = PLDM_ERROR_INVALID_LENGTH;
113*c8df31c1SAndrew Jeffery 		break;
114*c8df31c1SAndrew Jeffery 	default:
115*c8df31c1SAndrew Jeffery 		assert(false);
116*c8df31c1SAndrew Jeffery 		rc = PLDM_ERROR;
117*c8df31c1SAndrew Jeffery 		break;
118*c8df31c1SAndrew Jeffery 	}
119*c8df31c1SAndrew Jeffery 
120*c8df31c1SAndrew Jeffery 	assert(rc > 0);
121*c8df31c1SAndrew Jeffery 	return rc;
122*c8df31c1SAndrew Jeffery }
123*c8df31c1SAndrew Jeffery 
124c63f63a2SAndrew Jeffery /**
125c63f63a2SAndrew Jeffery  * @brief Initialize pldm buf struct for buf extractor
126c63f63a2SAndrew Jeffery  *
127c63f63a2SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
128c63f63a2SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
129c63f63a2SAndrew Jeffery  * @param[in] buf - buffer to be extracted
130c63f63a2SAndrew Jeffery  * @param[in] len - size of buffer
131c63f63a2SAndrew Jeffery  *
132*c8df31c1SAndrew Jeffery  * @return 0 on success, otherwise an error code appropriate for the current
133*c8df31c1SAndrew Jeffery  *         personality.
134c63f63a2SAndrew Jeffery  */
13576712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
136*c8df31c1SAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
137*c8df31c1SAndrew Jeffery pldm__msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
13876712f69SAndrew Jeffery 		  size_t len)
139c63f63a2SAndrew Jeffery {
140*c8df31c1SAndrew Jeffery 	assert(ctx);
141*c8df31c1SAndrew Jeffery 	assert(ctx->mode == PLDM_MSGBUF_PLDM_CC ||
142*c8df31c1SAndrew Jeffery 	       ctx->mode == PLDM_MSGBUF_C_ERRNO);
143*c8df31c1SAndrew Jeffery 
144*c8df31c1SAndrew Jeffery 	if (!buf) {
145*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
146c63f63a2SAndrew Jeffery 	}
147c63f63a2SAndrew Jeffery 
1482ff8cf89SAndrew Jeffery 	if ((minsize > len)) {
149*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
150c63f63a2SAndrew Jeffery 	}
151c63f63a2SAndrew Jeffery 
1522ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX
1532ff8cf89SAndrew Jeffery 	if (len > INTMAX_MAX) {
154*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1552ff8cf89SAndrew Jeffery 	}
1562ff8cf89SAndrew Jeffery #endif
1572ff8cf89SAndrew Jeffery 
15807febdbbSAndrew Jeffery 	if ((uintptr_t)buf + len < len) {
159*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
160c63f63a2SAndrew Jeffery 	}
161c63f63a2SAndrew Jeffery 
162c63f63a2SAndrew Jeffery 	ctx->cursor = (uint8_t *)buf;
1632ff8cf89SAndrew Jeffery 	ctx->remaining = (intmax_t)len;
164c63f63a2SAndrew Jeffery 
165*c8df31c1SAndrew Jeffery 	return 0;
166*c8df31c1SAndrew Jeffery }
167*c8df31c1SAndrew Jeffery 
168*c8df31c1SAndrew Jeffery /**
169*c8df31c1SAndrew Jeffery  * @brief Initialise a msgbuf instance to return errors as PLDM completion codes
170*c8df31c1SAndrew Jeffery  *
171*c8df31c1SAndrew Jeffery  * @see pldm__msgbuf_init
172*c8df31c1SAndrew Jeffery  *
173*c8df31c1SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
174*c8df31c1SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
175*c8df31c1SAndrew Jeffery  * @param[in] buf - buffer to be extracted
176*c8df31c1SAndrew Jeffery  * @param[in] len - size of buffer
177*c8df31c1SAndrew Jeffery  *
178*c8df31c1SAndrew Jeffery  * @return PLDM_SUCCESS if the provided buffer region is sensible,
179*c8df31c1SAndrew Jeffery  *         otherwise PLDM_ERROR_INVALID_DATA if pointer parameters are invalid,
180*c8df31c1SAndrew Jeffery  *         or PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
181*c8df31c1SAndrew Jeffery  */
182*c8df31c1SAndrew Jeffery __attribute__((always_inline)) static inline int
183*c8df31c1SAndrew Jeffery pldm_msgbuf_init_cc(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
184*c8df31c1SAndrew Jeffery 		    size_t len)
185*c8df31c1SAndrew Jeffery {
186*c8df31c1SAndrew Jeffery 	if (!ctx) {
187*c8df31c1SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
188*c8df31c1SAndrew Jeffery 	}
189*c8df31c1SAndrew Jeffery 
190*c8df31c1SAndrew Jeffery 	ctx->mode = PLDM_MSGBUF_PLDM_CC;
191*c8df31c1SAndrew Jeffery 	return pldm__msgbuf_init(ctx, minsize, buf, len);
192*c8df31c1SAndrew Jeffery }
193*c8df31c1SAndrew Jeffery 
194*c8df31c1SAndrew Jeffery /**
195*c8df31c1SAndrew Jeffery  * @brief Initialise a msgbuf instance to return errors as negative errno values
196*c8df31c1SAndrew Jeffery  *
197*c8df31c1SAndrew Jeffery  * @see pldm__msgbuf_init
198*c8df31c1SAndrew Jeffery  *
199*c8df31c1SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
200*c8df31c1SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
201*c8df31c1SAndrew Jeffery  * @param[in] buf - buffer to be extracted
202*c8df31c1SAndrew Jeffery  * @param[in] len - size of buffer
203*c8df31c1SAndrew Jeffery  *
204*c8df31c1SAndrew Jeffery  * @return 0 if the provided buffer region is sensible, otherwise -EINVAL if
205*c8df31c1SAndrew Jeffery  *         pointer parameters are invalid, or -EOVERFLOW if length constraints
206*c8df31c1SAndrew Jeffery  *         are violated.
207*c8df31c1SAndrew Jeffery  */
208*c8df31c1SAndrew Jeffery __attribute__((always_inline)) static inline int
209*c8df31c1SAndrew Jeffery pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
210*c8df31c1SAndrew Jeffery 		       size_t len)
211*c8df31c1SAndrew Jeffery {
212*c8df31c1SAndrew Jeffery 	if (!ctx) {
213*c8df31c1SAndrew Jeffery 		return -EINVAL;
214*c8df31c1SAndrew Jeffery 	}
215*c8df31c1SAndrew Jeffery 
216*c8df31c1SAndrew Jeffery 	ctx->mode = PLDM_MSGBUF_C_ERRNO;
217*c8df31c1SAndrew Jeffery 	return pldm__msgbuf_init(ctx, minsize, buf, len);
218c63f63a2SAndrew Jeffery }
219c63f63a2SAndrew Jeffery 
220c63f63a2SAndrew Jeffery /**
221c63f63a2SAndrew Jeffery  * @brief Validate buffer overflow state
222c63f63a2SAndrew Jeffery  *
223c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
224c63f63a2SAndrew Jeffery  *
225c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
226c63f63a2SAndrew Jeffery  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
227c63f63a2SAndrew Jeffery  * prior accesses would have occurred beyond the bounds of the buffer, and
228c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
229c63f63a2SAndrew Jeffery  * pointer.
230c63f63a2SAndrew Jeffery  */
23176712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
23276712f69SAndrew Jeffery pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
233c63f63a2SAndrew Jeffery {
234*c8df31c1SAndrew Jeffery 	assert(ctx);
235*c8df31c1SAndrew Jeffery 	if (ctx->remaining < 0) {
236*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
237c63f63a2SAndrew Jeffery 	}
238c63f63a2SAndrew Jeffery 
239*c8df31c1SAndrew Jeffery 	return 0;
240c63f63a2SAndrew Jeffery }
241c63f63a2SAndrew Jeffery 
242c63f63a2SAndrew Jeffery /**
243db7b8324SAndrew Jeffery  * @brief Test whether a message buffer has been exactly consumed
244db7b8324SAndrew Jeffery  *
245db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
246db7b8324SAndrew Jeffery  *
247db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
248db7b8324SAndrew Jeffery  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
249db7b8324SAndrew Jeffery  * indicates that an incorrect sequence of accesses have occurred, and
250db7b8324SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
251db7b8324SAndrew Jeffery  * pointer.
252db7b8324SAndrew Jeffery  */
25376712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
25476712f69SAndrew Jeffery pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
255db7b8324SAndrew Jeffery {
256*c8df31c1SAndrew Jeffery 	assert(ctx);
257*c8df31c1SAndrew Jeffery 	if (ctx->remaining != 0) {
258*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EBADMSG);
259db7b8324SAndrew Jeffery 	}
260db7b8324SAndrew Jeffery 
261*c8df31c1SAndrew Jeffery 	return 0;
262db7b8324SAndrew Jeffery }
263db7b8324SAndrew Jeffery 
264db7b8324SAndrew Jeffery /**
265c63f63a2SAndrew Jeffery  * @brief Destroy the pldm buf
266c63f63a2SAndrew Jeffery  *
267c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
268c63f63a2SAndrew Jeffery  *
269c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
270c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
271c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
272c63f63a2SAndrew Jeffery  * bounds of the buffer.
273c63f63a2SAndrew Jeffery  */
27476712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
27576712f69SAndrew Jeffery pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
276c63f63a2SAndrew Jeffery {
277c63f63a2SAndrew Jeffery 	int valid;
278c63f63a2SAndrew Jeffery 
279*c8df31c1SAndrew Jeffery 	assert(ctx);
280c63f63a2SAndrew Jeffery 	valid = pldm_msgbuf_validate(ctx);
281c63f63a2SAndrew Jeffery 
282c63f63a2SAndrew Jeffery 	ctx->cursor = NULL;
283c63f63a2SAndrew Jeffery 	ctx->remaining = 0;
284c63f63a2SAndrew Jeffery 
285c63f63a2SAndrew Jeffery 	return valid;
286c63f63a2SAndrew Jeffery }
287c63f63a2SAndrew Jeffery 
288c63f63a2SAndrew Jeffery /**
289db7b8324SAndrew Jeffery  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
290db7b8324SAndrew Jeffery  * has been completely consumed without overflow
291db7b8324SAndrew Jeffery  *
292db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context
293db7b8324SAndrew Jeffery  *
294db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
295db7b8324SAndrew Jeffery  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
296db7b8324SAndrew Jeffery  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
297db7b8324SAndrew Jeffery  * have occurred byond the bounds of the buffer
298db7b8324SAndrew Jeffery  */
29976712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
30076712f69SAndrew Jeffery pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
301db7b8324SAndrew Jeffery {
302db7b8324SAndrew Jeffery 	int consumed;
303db7b8324SAndrew Jeffery 
304*c8df31c1SAndrew Jeffery 	assert(ctx);
305db7b8324SAndrew Jeffery 	consumed = pldm_msgbuf_consumed(ctx);
306db7b8324SAndrew Jeffery 
307db7b8324SAndrew Jeffery 	ctx->cursor = NULL;
308db7b8324SAndrew Jeffery 	ctx->remaining = 0;
309db7b8324SAndrew Jeffery 
310db7b8324SAndrew Jeffery 	return consumed;
311db7b8324SAndrew Jeffery }
312db7b8324SAndrew Jeffery 
31366c7723aSAndrew Jeffery /*
31466c7723aSAndrew Jeffery  * Exploit the pre-processor to perform type checking by macro substitution.
31566c7723aSAndrew Jeffery  *
31666c7723aSAndrew Jeffery  * A C type is defined by its alignment as well as its object
31766c7723aSAndrew Jeffery  * size, and compilers have a hammer to enforce it in the form of
31866c7723aSAndrew Jeffery  * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
31966c7723aSAndrew Jeffery  * the libpldm public API this presents a problem: Naively attempting to use the
32066c7723aSAndrew Jeffery  * msgbuf APIs on a member of a packed struct would yield an error.
32166c7723aSAndrew Jeffery  *
32266c7723aSAndrew Jeffery  * The msgbuf APIs are implemented such that data is moved through unaligned
32366c7723aSAndrew Jeffery  * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
32466c7723aSAndrew Jeffery  * make the object pointers take a trip through `void *` at its API boundary.
32566c7723aSAndrew Jeffery  * That presents a bit too much of an opportunity to non-surgically remove your
32666c7723aSAndrew Jeffery  * own foot, so here we set about doing something to mitigate that as well.
32766c7723aSAndrew Jeffery  *
32866c7723aSAndrew Jeffery  * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
32966c7723aSAndrew Jeffery  * only for the purpose of object sizes, disregarding alignment. We have a few
33066c7723aSAndrew Jeffery  * constraints that cause some headaches:
33166c7723aSAndrew Jeffery  *
33266c7723aSAndrew Jeffery  * 1. We have to perform the type-check before a call through a C function,
33366c7723aSAndrew Jeffery  *    as the function must take the object pointer argument as `void *`.
33466c7723aSAndrew Jeffery  *    Essentially, this constrains us to doing something with macros.
33566c7723aSAndrew Jeffery  *
33666c7723aSAndrew Jeffery  * 2. While libpldm is a C library, its test suite is written in C++ to take
33766c7723aSAndrew Jeffery  *    advantage of gtest.
33866c7723aSAndrew Jeffery  *
33966c7723aSAndrew Jeffery  * 3. Ideally we'd do something with C's `static_assert()`, however
34066c7723aSAndrew Jeffery  *    `static_assert()` is defined as void, and as we're constrained to macros,
34166c7723aSAndrew Jeffery  *    using `static_assert()` would require a statement-expression
34266c7723aSAndrew Jeffery  *
34366c7723aSAndrew Jeffery  * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
34466c7723aSAndrew Jeffery  *    are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
34566c7723aSAndrew Jeffery  *    the purpose of enabling statement-expressions in this one instance.
34666c7723aSAndrew Jeffery  *
34766c7723aSAndrew Jeffery  * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
34866c7723aSAndrew Jeffery  *    however it's implemented in terms of `_Generic()`, which is not available
34966c7723aSAndrew Jeffery  *    in C++.
35066c7723aSAndrew Jeffery  *
35166c7723aSAndrew Jeffery  * Combined this means we need separate solutions for C and C++.
35266c7723aSAndrew Jeffery  *
35366c7723aSAndrew Jeffery  * For C, as we don't have statement-expressions, we need to exploit some other
35466c7723aSAndrew Jeffery  * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
35566c7723aSAndrew Jeffery  * API function call. We also have to take care of the fact that the call-sites
35666c7723aSAndrew Jeffery  * may be in the context of a variable assignment for error-handling purposes.
35766c7723aSAndrew Jeffery  * The key observation is that we can use the comma operator as a sequence point
35866c7723aSAndrew Jeffery  * to order the type check before the API call, discarding the "result" value of
35966c7723aSAndrew Jeffery  * the type check and yielding the return value of the API call.
36066c7723aSAndrew Jeffery  *
36166c7723aSAndrew Jeffery  * C++ could be less of a headache than the C as we can leverage template
36266c7723aSAndrew Jeffery  * functions. An advantage of template functions is that while their definition
36366c7723aSAndrew Jeffery  * is driven by instantion, the definition does not appear at the source
36466c7723aSAndrew Jeffery  * location of the instantation, which gives it a great leg-up over the problems
36566c7723aSAndrew Jeffery  * we have in the C path. However, the use of the msgbuf APIs in the test suite
36666c7723aSAndrew Jeffery  * still makes things somewhat tricky, as the call-sites in the test suite are
36766c7723aSAndrew Jeffery  * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
36866c7723aSAndrew Jeffery  * takes both the object type and the required type as template arguments, and
36966c7723aSAndrew Jeffery  * then define the object pointer parameter as `void *` for a call through to
37066c7723aSAndrew Jeffery  * the appropriate msgbuf API. However, because the msgbuf API call-sites are
37166c7723aSAndrew Jeffery  * encapsulated in gtest macros, use of commas in the template specification
37266c7723aSAndrew Jeffery  * causes pre-processor confusion. In this way we're constrained to only one
37366c7723aSAndrew Jeffery  * template argument per function.
37466c7723aSAndrew Jeffery  *
37566c7723aSAndrew Jeffery  * Implement the C++ path using template functions that take the destination
37666c7723aSAndrew Jeffery  * object type as a template argument, while the name of the function symbols
37766c7723aSAndrew Jeffery  * are derived from the required type. The manual implementations of these
37866c7723aSAndrew Jeffery  * appear at the end of the header. The type safety is actually enforced
37966c7723aSAndrew Jeffery  * by `static_assert()` this time, as we can use statements as we're not
38066c7723aSAndrew Jeffery  * constrained to an expression in the templated function body.
38166c7723aSAndrew Jeffery  *
38266c7723aSAndrew Jeffery  * The invocations of pldm_msgbuf_extract_typecheck() typically result in
38366c7723aSAndrew Jeffery  * double-evaluation of some arguments. We're not yet bothered by this for two
38466c7723aSAndrew Jeffery  * reasons:
38566c7723aSAndrew Jeffery  *
38666c7723aSAndrew Jeffery  * 1. The nature of the current call-sites are such that there are no
38766c7723aSAndrew Jeffery  *    argument expressions that result in undesirable side-effects
38866c7723aSAndrew Jeffery  *
38966c7723aSAndrew Jeffery  * 2. It's an API internal to the libpldm implementation, and we can fix things
39066c7723aSAndrew Jeffery  *    whenever something crops up the violates the observation in 1.
39166c7723aSAndrew Jeffery  */
39266c7723aSAndrew Jeffery #ifdef __cplusplus
39366c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
39466c7723aSAndrew Jeffery 	pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
39566c7723aSAndrew Jeffery #else
39666c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
39766c7723aSAndrew Jeffery 	(pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
39866c7723aSAndrew Jeffery #endif
39966c7723aSAndrew Jeffery 
400db7b8324SAndrew Jeffery /**
401c63f63a2SAndrew Jeffery  * @brief pldm_msgbuf extractor for a uint8_t
402c63f63a2SAndrew Jeffery  *
403c63f63a2SAndrew Jeffery  * @param[inout] ctx - pldm_msgbuf context for extractor
404c63f63a2SAndrew Jeffery  * @param[out] dst - destination of extracted value
405c63f63a2SAndrew Jeffery  *
406c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
407c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH otherwise.
408c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
409c63f63a2SAndrew Jeffery  */
41066c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint8(ctx, dst)                                    \
41166c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8,     \
41266c7723aSAndrew Jeffery 				      dst, ctx, dst)
41376712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
41466c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
41576712f69SAndrew Jeffery pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
416c63f63a2SAndrew Jeffery {
417*c8df31c1SAndrew Jeffery 	assert(ctx);
418*c8df31c1SAndrew Jeffery 
419*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
420*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
421c63f63a2SAndrew Jeffery 	}
422c63f63a2SAndrew Jeffery 
4232ff8cf89SAndrew Jeffery 	if (ctx->remaining == INTMAX_MIN) {
4242ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
425*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
4262ff8cf89SAndrew Jeffery 	}
42766c7723aSAndrew Jeffery 	ctx->remaining -= sizeof(uint8_t);
428c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
429c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
430*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
431c63f63a2SAndrew Jeffery 	}
432c63f63a2SAndrew Jeffery 
43366c7723aSAndrew Jeffery 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
43466c7723aSAndrew Jeffery 
435c63f63a2SAndrew Jeffery 	ctx->cursor++;
436*c8df31c1SAndrew Jeffery 	return 0;
437c63f63a2SAndrew Jeffery }
438c63f63a2SAndrew Jeffery 
43966c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
44066c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
44166c7723aSAndrew Jeffery 				      ctx, dst)
44276712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
44366c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
44476712f69SAndrew Jeffery pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
445c63f63a2SAndrew Jeffery {
446*c8df31c1SAndrew Jeffery 	assert(ctx);
447*c8df31c1SAndrew Jeffery 
448*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
449*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
450c63f63a2SAndrew Jeffery 	}
451c63f63a2SAndrew Jeffery 
4522ff8cf89SAndrew Jeffery 	if (ctx->remaining == INTMAX_MIN) {
4532ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
454*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
4552ff8cf89SAndrew Jeffery 	}
45666c7723aSAndrew Jeffery 	ctx->remaining -= sizeof(int8_t);
457c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
458c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
459*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
460c63f63a2SAndrew Jeffery 	}
461c63f63a2SAndrew Jeffery 
46266c7723aSAndrew Jeffery 	memcpy(dst, ctx->cursor, sizeof(int8_t));
463c63f63a2SAndrew Jeffery 	ctx->cursor++;
464*c8df31c1SAndrew Jeffery 	return 0;
465c63f63a2SAndrew Jeffery }
466c63f63a2SAndrew Jeffery 
46766c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
46866c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
46966c7723aSAndrew Jeffery 				      dst, ctx, dst)
47076712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
47166c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
47276712f69SAndrew Jeffery pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst)
473c63f63a2SAndrew Jeffery {
474c63f63a2SAndrew Jeffery 	uint16_t ldst;
475c63f63a2SAndrew Jeffery 
476*c8df31c1SAndrew Jeffery 	assert(ctx);
477*c8df31c1SAndrew Jeffery 
478*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
479*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
480c63f63a2SAndrew Jeffery 	}
481c63f63a2SAndrew Jeffery 
4822ff8cf89SAndrew Jeffery 	// Check for underflow while tracking the magnitude of the buffer overflow
4832ff8cf89SAndrew Jeffery 	static_assert(
4842ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
4852ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
4862ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
4872ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
4882ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
489*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
4902ff8cf89SAndrew Jeffery 	}
4912ff8cf89SAndrew Jeffery 
492c63f63a2SAndrew Jeffery 	// Check for buffer overflow. If we overflow, account for the request as
493c63f63a2SAndrew Jeffery 	// negative values in ctx->remaining. This way we can debug how far
494c63f63a2SAndrew Jeffery 	// we've overflowed.
495c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
496c63f63a2SAndrew Jeffery 
497c63f63a2SAndrew Jeffery 	// Prevent the access if it would overflow. First, assert so we blow up
498c63f63a2SAndrew Jeffery 	// the test suite right at the point of failure. However, cater to
499c63f63a2SAndrew Jeffery 	// -DNDEBUG by explicitly testing that the access is valid.
500c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
501c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
502*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
503c63f63a2SAndrew Jeffery 	}
504c63f63a2SAndrew Jeffery 
505c63f63a2SAndrew Jeffery 	// Use memcpy() to have the compiler deal with any alignment
506c63f63a2SAndrew Jeffery 	// issues on the target architecture
507c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
508c63f63a2SAndrew Jeffery 
509c63f63a2SAndrew Jeffery 	// Only assign the target value once it's correctly decoded
51066c7723aSAndrew Jeffery 	ldst = le16toh(ldst);
51166c7723aSAndrew Jeffery 
51266c7723aSAndrew Jeffery 	// Allow storing to unaligned
51366c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
51466c7723aSAndrew Jeffery 
515c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
516c63f63a2SAndrew Jeffery 
517*c8df31c1SAndrew Jeffery 	return 0;
518c63f63a2SAndrew Jeffery }
519c63f63a2SAndrew Jeffery 
52066c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
52166c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
52266c7723aSAndrew Jeffery 				      dst, ctx, dst)
52376712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
52466c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
52576712f69SAndrew Jeffery pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
526c63f63a2SAndrew Jeffery {
527c63f63a2SAndrew Jeffery 	int16_t ldst;
528c63f63a2SAndrew Jeffery 
529*c8df31c1SAndrew Jeffery 	assert(ctx);
530*c8df31c1SAndrew Jeffery 
531*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
532*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
533c63f63a2SAndrew Jeffery 	}
534c63f63a2SAndrew Jeffery 
5352ff8cf89SAndrew Jeffery 	static_assert(
5362ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
5372ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
5382ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
5392ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
5402ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
541*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
5422ff8cf89SAndrew Jeffery 	}
543c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
544c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
545c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
546*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
547c63f63a2SAndrew Jeffery 	}
548c63f63a2SAndrew Jeffery 
549c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
550c63f63a2SAndrew Jeffery 
55166c7723aSAndrew Jeffery 	ldst = le16toh(ldst);
55266c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
553c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
554c63f63a2SAndrew Jeffery 
555*c8df31c1SAndrew Jeffery 	return 0;
556c63f63a2SAndrew Jeffery }
557c63f63a2SAndrew Jeffery 
55866c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
55966c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
56066c7723aSAndrew Jeffery 				      dst, ctx, dst)
56176712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
56266c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
56376712f69SAndrew Jeffery pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst)
564c63f63a2SAndrew Jeffery {
565c63f63a2SAndrew Jeffery 	uint32_t ldst;
566c63f63a2SAndrew Jeffery 
567*c8df31c1SAndrew Jeffery 	assert(ctx);
568*c8df31c1SAndrew Jeffery 
569*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
570*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
571c63f63a2SAndrew Jeffery 	}
572c63f63a2SAndrew Jeffery 
5732ff8cf89SAndrew Jeffery 	static_assert(
5742ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
5752ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
5762ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
5772ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
5782ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
579*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
5802ff8cf89SAndrew Jeffery 	}
581c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
582c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
583c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
584*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
585c63f63a2SAndrew Jeffery 	}
586c63f63a2SAndrew Jeffery 
587c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
58866c7723aSAndrew Jeffery 	ldst = le32toh(ldst);
58966c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
590c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
591c63f63a2SAndrew Jeffery 
592*c8df31c1SAndrew Jeffery 	return 0;
593c63f63a2SAndrew Jeffery }
594c63f63a2SAndrew Jeffery 
59566c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
59666c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
59766c7723aSAndrew Jeffery 				      dst, ctx, dst)
59876712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
59966c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
60076712f69SAndrew Jeffery pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
601c63f63a2SAndrew Jeffery {
602c63f63a2SAndrew Jeffery 	int32_t ldst;
603c63f63a2SAndrew Jeffery 
604*c8df31c1SAndrew Jeffery 	assert(ctx);
605*c8df31c1SAndrew Jeffery 
606*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
607*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
608c63f63a2SAndrew Jeffery 	}
609c63f63a2SAndrew Jeffery 
6102ff8cf89SAndrew Jeffery 	static_assert(
6112ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
6122ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
6132ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
6142ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
6152ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
616*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
6172ff8cf89SAndrew Jeffery 	}
618c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
619c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
620c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
621*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
622c63f63a2SAndrew Jeffery 	}
623c63f63a2SAndrew Jeffery 
624c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
62566c7723aSAndrew Jeffery 	ldst = le32toh(ldst);
62666c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
627c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
628c63f63a2SAndrew Jeffery 
629c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
630c63f63a2SAndrew Jeffery }
631c63f63a2SAndrew Jeffery 
63266c7723aSAndrew Jeffery #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
63366c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
63466c7723aSAndrew Jeffery 				      dst, ctx, dst)
63576712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
63666c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
63776712f69SAndrew Jeffery pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst)
638c63f63a2SAndrew Jeffery {
639c63f63a2SAndrew Jeffery 	uint32_t ldst;
640c63f63a2SAndrew Jeffery 
641*c8df31c1SAndrew Jeffery 	static_assert(sizeof(real32_t) == sizeof(ldst),
64266c7723aSAndrew Jeffery 		      "Mismatched type sizes for dst and ldst");
64366c7723aSAndrew Jeffery 
644*c8df31c1SAndrew Jeffery 	assert(ctx);
645*c8df31c1SAndrew Jeffery 
646*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
647*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
648c63f63a2SAndrew Jeffery 	}
649c63f63a2SAndrew Jeffery 
6502ff8cf89SAndrew Jeffery 	static_assert(
6512ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
6522ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
6532ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
6542ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
6552ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
656*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
6572ff8cf89SAndrew Jeffery 	}
658c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
659c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
660c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
661*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
662c63f63a2SAndrew Jeffery 	}
663c63f63a2SAndrew Jeffery 
664c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
665c63f63a2SAndrew Jeffery 	ldst = le32toh(ldst);
66666c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
66766c7723aSAndrew Jeffery 	ctx->cursor += sizeof(ldst);
668c63f63a2SAndrew Jeffery 
669*c8df31c1SAndrew Jeffery 	return 0;
670c63f63a2SAndrew Jeffery }
671c63f63a2SAndrew Jeffery 
67266c7723aSAndrew Jeffery /**
67366c7723aSAndrew Jeffery  * Extract the field at the msgbuf cursor into the lvalue named by dst.
67466c7723aSAndrew Jeffery  *
67566c7723aSAndrew Jeffery  * @param ctx The msgbuf context object
67666c7723aSAndrew Jeffery  * @param dst The lvalue into which the field at the msgbuf cursor should be
67766c7723aSAndrew Jeffery  *            extracted
67866c7723aSAndrew Jeffery  *
67966c7723aSAndrew Jeffery  * @return PLDM_SUCCESS on success, otherwise another value on error
68066c7723aSAndrew Jeffery  */
681c63f63a2SAndrew Jeffery #define pldm_msgbuf_extract(ctx, dst)                                          \
68266c7723aSAndrew Jeffery 	_Generic((dst),                                                        \
68366c7723aSAndrew Jeffery 		uint8_t: pldm__msgbuf_extract_uint8,                           \
68466c7723aSAndrew Jeffery 		int8_t: pldm__msgbuf_extract_int8,                             \
68566c7723aSAndrew Jeffery 		uint16_t: pldm__msgbuf_extract_uint16,                         \
68666c7723aSAndrew Jeffery 		int16_t: pldm__msgbuf_extract_int16,                           \
68766c7723aSAndrew Jeffery 		uint32_t: pldm__msgbuf_extract_uint32,                         \
68866c7723aSAndrew Jeffery 		int32_t: pldm__msgbuf_extract_int32,                           \
68966c7723aSAndrew Jeffery 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
69066c7723aSAndrew Jeffery 
69166c7723aSAndrew Jeffery /**
69266c7723aSAndrew Jeffery  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
69366c7723aSAndrew Jeffery  *
69466c7723aSAndrew Jeffery  * @param ctx The msgbuf context object
69566c7723aSAndrew Jeffery  * @param dst The pointer to the object into which the field at the msgbuf
69666c7723aSAndrew Jeffery  *            cursor should be extracted
69766c7723aSAndrew Jeffery  *
69866c7723aSAndrew Jeffery  * @return PLDM_SUCCESS on success, otherwise another value on error
69966c7723aSAndrew Jeffery  */
70066c7723aSAndrew Jeffery #define pldm_msgbuf_extract_p(ctx, dst)                                        \
70166c7723aSAndrew Jeffery 	_Generic((dst),                                                        \
70266c7723aSAndrew Jeffery 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
70366c7723aSAndrew Jeffery 		int8_t *: pldm__msgbuf_extract_int8,                           \
70466c7723aSAndrew Jeffery 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
70566c7723aSAndrew Jeffery 		int16_t *: pldm__msgbuf_extract_int16,                         \
70666c7723aSAndrew Jeffery 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
70766c7723aSAndrew Jeffery 		int32_t *: pldm__msgbuf_extract_int32,                         \
70866c7723aSAndrew Jeffery 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
709c63f63a2SAndrew Jeffery 
71076712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
71176712f69SAndrew Jeffery pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, uint8_t *dst,
71276712f69SAndrew Jeffery 				size_t count)
713369b121aSAndrew Jeffery {
714*c8df31c1SAndrew Jeffery 	assert(ctx);
715*c8df31c1SAndrew Jeffery 
716*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
717*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
718369b121aSAndrew Jeffery 	}
719369b121aSAndrew Jeffery 
720369b121aSAndrew Jeffery 	if (!count) {
721*c8df31c1SAndrew Jeffery 		return 0;
722369b121aSAndrew Jeffery 	}
723369b121aSAndrew Jeffery 
7242ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX
7252ff8cf89SAndrew Jeffery 	if (count > INTMAX_MAX) {
726*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
727369b121aSAndrew Jeffery 	}
7282ff8cf89SAndrew Jeffery #endif
729369b121aSAndrew Jeffery 
7302ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
731*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
7322ff8cf89SAndrew Jeffery 	}
7332ff8cf89SAndrew Jeffery 	ctx->remaining -= (intmax_t)count;
734369b121aSAndrew Jeffery 	assert(ctx->remaining >= 0);
735369b121aSAndrew Jeffery 	if (ctx->remaining < 0) {
736*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
737369b121aSAndrew Jeffery 	}
738369b121aSAndrew Jeffery 
739a065eccbSAndrew Jeffery 	memcpy(dst, ctx->cursor, count);
740a065eccbSAndrew Jeffery 	ctx->cursor += count;
741369b121aSAndrew Jeffery 
742*c8df31c1SAndrew Jeffery 	return 0;
743369b121aSAndrew Jeffery }
744369b121aSAndrew Jeffery 
745369b121aSAndrew Jeffery #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
74637dd6a3dSAndrew Jeffery 	_Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
74737dd6a3dSAndrew Jeffery 								     count)
748369b121aSAndrew Jeffery 
74976712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
75076712f69SAndrew Jeffery pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, const uint32_t src)
751062c8762SThu Nguyen {
752062c8762SThu Nguyen 	uint32_t val = htole32(src);
753062c8762SThu Nguyen 
754*c8df31c1SAndrew Jeffery 	assert(ctx);
755*c8df31c1SAndrew Jeffery 
756*c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
757*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
758062c8762SThu Nguyen 	}
759062c8762SThu Nguyen 
7602ff8cf89SAndrew Jeffery 	static_assert(
7612ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
7622ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
7632ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
7642ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
7652ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
766*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
7672ff8cf89SAndrew Jeffery 	}
768062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
769062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
770062c8762SThu Nguyen 	if (ctx->remaining < 0) {
771*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
772062c8762SThu Nguyen 	}
773062c8762SThu Nguyen 
774062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
775062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
776062c8762SThu Nguyen 
777*c8df31c1SAndrew Jeffery 	return 0;
778062c8762SThu Nguyen }
779062c8762SThu Nguyen 
78076712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
78176712f69SAndrew Jeffery pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, const uint16_t src)
782062c8762SThu Nguyen {
783062c8762SThu Nguyen 	uint16_t val = htole16(src);
784062c8762SThu Nguyen 
785*c8df31c1SAndrew Jeffery 	assert(ctx);
786*c8df31c1SAndrew Jeffery 
787*c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
788*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
789062c8762SThu Nguyen 	}
790062c8762SThu Nguyen 
7912ff8cf89SAndrew Jeffery 	static_assert(
7922ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
7932ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
7942ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
7952ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
7962ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
797*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
7982ff8cf89SAndrew Jeffery 	}
799062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
800062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
801062c8762SThu Nguyen 	if (ctx->remaining < 0) {
802*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
803062c8762SThu Nguyen 	}
804062c8762SThu Nguyen 
805062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
806062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
807062c8762SThu Nguyen 
808*c8df31c1SAndrew Jeffery 	return 0;
809062c8762SThu Nguyen }
810062c8762SThu Nguyen 
81176712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
81276712f69SAndrew Jeffery pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, const uint8_t src)
813062c8762SThu Nguyen {
814*c8df31c1SAndrew Jeffery 	assert(ctx);
815*c8df31c1SAndrew Jeffery 
816*c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
817*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
818062c8762SThu Nguyen 	}
819062c8762SThu Nguyen 
8202ff8cf89SAndrew Jeffery 	static_assert(
8212ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
8222ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
8232ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
8242ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
8252ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
826*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
8272ff8cf89SAndrew Jeffery 	}
828062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
829062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
830062c8762SThu Nguyen 	if (ctx->remaining < 0) {
831*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
832062c8762SThu Nguyen 	}
833062c8762SThu Nguyen 
834062c8762SThu Nguyen 	memcpy(ctx->cursor, &src, sizeof(src));
835062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
836062c8762SThu Nguyen 
837*c8df31c1SAndrew Jeffery 	return 0;
838062c8762SThu Nguyen }
839062c8762SThu Nguyen 
84076712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
84176712f69SAndrew Jeffery pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, const int32_t src)
842062c8762SThu Nguyen {
843062c8762SThu Nguyen 	int32_t val = htole32(src);
844062c8762SThu Nguyen 
845*c8df31c1SAndrew Jeffery 	assert(ctx);
846*c8df31c1SAndrew Jeffery 
847*c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
848*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
849062c8762SThu Nguyen 	}
850062c8762SThu Nguyen 
8512ff8cf89SAndrew Jeffery 	static_assert(
8522ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
8532ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
8542ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
8552ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
8562ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
857*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
8582ff8cf89SAndrew Jeffery 	}
859062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
860062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
861062c8762SThu Nguyen 	if (ctx->remaining < 0) {
862*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
863062c8762SThu Nguyen 	}
864062c8762SThu Nguyen 
865062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
866062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
867062c8762SThu Nguyen 
868*c8df31c1SAndrew Jeffery 	return 0;
869062c8762SThu Nguyen }
870062c8762SThu Nguyen 
87176712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
87276712f69SAndrew Jeffery pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, const int16_t src)
873062c8762SThu Nguyen {
874062c8762SThu Nguyen 	int16_t val = htole16(src);
875062c8762SThu Nguyen 
876*c8df31c1SAndrew Jeffery 	assert(ctx);
877*c8df31c1SAndrew Jeffery 
878*c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
879*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
880062c8762SThu Nguyen 	}
881062c8762SThu Nguyen 
8822ff8cf89SAndrew Jeffery 	static_assert(
8832ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
8842ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
8852ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
8862ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
8872ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
888*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
8892ff8cf89SAndrew Jeffery 	}
890062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
891062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
892062c8762SThu Nguyen 	if (ctx->remaining < 0) {
893*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
894062c8762SThu Nguyen 	}
895062c8762SThu Nguyen 
896062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
897062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
898062c8762SThu Nguyen 
899*c8df31c1SAndrew Jeffery 	return 0;
900062c8762SThu Nguyen }
901062c8762SThu Nguyen 
90276712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
90376712f69SAndrew Jeffery pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, const int8_t src)
904062c8762SThu Nguyen {
905*c8df31c1SAndrew Jeffery 	assert(ctx);
906*c8df31c1SAndrew Jeffery 
907*c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
908*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
909062c8762SThu Nguyen 	}
910062c8762SThu Nguyen 
9112ff8cf89SAndrew Jeffery 	static_assert(
9122ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
9132ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
9142ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
9152ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
9162ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
917*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
9182ff8cf89SAndrew Jeffery 	}
919062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
920062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
921062c8762SThu Nguyen 	if (ctx->remaining < 0) {
922*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
923062c8762SThu Nguyen 	}
924062c8762SThu Nguyen 
925062c8762SThu Nguyen 	memcpy(ctx->cursor, &src, sizeof(src));
926062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
927062c8762SThu Nguyen 
928*c8df31c1SAndrew Jeffery 	return 0;
929062c8762SThu Nguyen }
930062c8762SThu Nguyen 
931062c8762SThu Nguyen #define pldm_msgbuf_insert(dst, src)                                           \
93237dd6a3dSAndrew Jeffery 	_Generic((src),                                                        \
93337dd6a3dSAndrew Jeffery 		uint8_t: pldm_msgbuf_insert_uint8,                             \
93437dd6a3dSAndrew Jeffery 		int8_t: pldm_msgbuf_insert_int8,                               \
93537dd6a3dSAndrew Jeffery 		uint16_t: pldm_msgbuf_insert_uint16,                           \
93637dd6a3dSAndrew Jeffery 		int16_t: pldm_msgbuf_insert_int16,                             \
93737dd6a3dSAndrew Jeffery 		uint32_t: pldm_msgbuf_insert_uint32,                           \
93837dd6a3dSAndrew Jeffery 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
939062c8762SThu Nguyen 
94076712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
94176712f69SAndrew Jeffery pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, const uint8_t *src,
942062c8762SThu Nguyen 			       size_t count)
943062c8762SThu Nguyen {
944*c8df31c1SAndrew Jeffery 	assert(ctx);
945*c8df31c1SAndrew Jeffery 
946*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !src) {
947*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
948062c8762SThu Nguyen 	}
949062c8762SThu Nguyen 
950062c8762SThu Nguyen 	if (!count) {
951*c8df31c1SAndrew Jeffery 		return 0;
952062c8762SThu Nguyen 	}
953062c8762SThu Nguyen 
9542ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX
9552ff8cf89SAndrew Jeffery 	if (count > INTMAX_MAX) {
956*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
957062c8762SThu Nguyen 	}
9582ff8cf89SAndrew Jeffery #endif
959062c8762SThu Nguyen 
9602ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
961*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
9622ff8cf89SAndrew Jeffery 	}
9632ff8cf89SAndrew Jeffery 	ctx->remaining -= (intmax_t)count;
964062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
965062c8762SThu Nguyen 	if (ctx->remaining < 0) {
966*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
967062c8762SThu Nguyen 	}
968062c8762SThu Nguyen 
969a065eccbSAndrew Jeffery 	memcpy(ctx->cursor, src, count);
970a065eccbSAndrew Jeffery 	ctx->cursor += count;
971062c8762SThu Nguyen 
972*c8df31c1SAndrew Jeffery 	return 0;
973062c8762SThu Nguyen }
974062c8762SThu Nguyen 
975062c8762SThu Nguyen #define pldm_msgbuf_insert_array(dst, src, count)                              \
97637dd6a3dSAndrew Jeffery 	_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src,  \
97737dd6a3dSAndrew Jeffery 								    count)
978062c8762SThu Nguyen 
97976712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
98076712f69SAndrew Jeffery pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, size_t required,
98176712f69SAndrew Jeffery 			  void **cursor)
982062c8762SThu Nguyen {
983*c8df31c1SAndrew Jeffery 	assert(ctx);
984*c8df31c1SAndrew Jeffery 
985*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !cursor || *cursor) {
986*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
987062c8762SThu Nguyen 	}
988062c8762SThu Nguyen 
9892ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX
9902ff8cf89SAndrew Jeffery 	if (required > INTMAX_MAX) {
991*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
992062c8762SThu Nguyen 	}
9932ff8cf89SAndrew Jeffery #endif
994062c8762SThu Nguyen 
9952ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
996*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
9972ff8cf89SAndrew Jeffery 	}
9982ff8cf89SAndrew Jeffery 	ctx->remaining -= (intmax_t)required;
999062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
1000062c8762SThu Nguyen 	if (ctx->remaining < 0) {
1001*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1002062c8762SThu Nguyen 	}
1003062c8762SThu Nguyen 
1004062c8762SThu Nguyen 	*cursor = ctx->cursor;
1005062c8762SThu Nguyen 	ctx->cursor += required;
1006062c8762SThu Nguyen 
1007*c8df31c1SAndrew Jeffery 	return 0;
1008062c8762SThu Nguyen }
1009062c8762SThu Nguyen 
101076712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
101176712f69SAndrew Jeffery pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
1012062c8762SThu Nguyen {
1013*c8df31c1SAndrew Jeffery 	assert(ctx);
1014*c8df31c1SAndrew Jeffery 
1015*c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !cursor || *cursor || !len) {
1016*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
1017062c8762SThu Nguyen 	}
1018062c8762SThu Nguyen 
1019062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
1020062c8762SThu Nguyen 	if (ctx->remaining < 0) {
1021*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1022062c8762SThu Nguyen 	}
1023062c8762SThu Nguyen 
1024062c8762SThu Nguyen 	*cursor = ctx->cursor;
1025062c8762SThu Nguyen 	ctx->cursor += ctx->remaining;
1026062c8762SThu Nguyen 	*len = ctx->remaining;
1027062c8762SThu Nguyen 	ctx->remaining = 0;
1028062c8762SThu Nguyen 
1029*c8df31c1SAndrew Jeffery 	return 0;
1030062c8762SThu Nguyen }
1031909bf7c2SVarsha Kaverappa 
1032909bf7c2SVarsha Kaverappa /**
1033909bf7c2SVarsha Kaverappa  * @brief pldm_msgbuf copy data between two msg buffers
1034909bf7c2SVarsha Kaverappa  *
1035909bf7c2SVarsha Kaverappa  * @param[inout] src - pldm_msgbuf for source from where value should be copied
1036909bf7c2SVarsha Kaverappa  * @param[inout] dst - destination of copy from source
1037909bf7c2SVarsha Kaverappa  * @param[in] size - size of data to be copied
1038909bf7c2SVarsha Kaverappa  * @param[in] description - description of data copied
1039909bf7c2SVarsha Kaverappa  *
1040909bf7c2SVarsha Kaverappa  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1041909bf7c2SVarsha Kaverappa  * PLDM_ERROR_INVALID_LENGTH otherwise.
1042909bf7c2SVarsha Kaverappa  * PLDM_ERROR_INVALID_DATA if input is invalid
1043909bf7c2SVarsha Kaverappa  */
1044909bf7c2SVarsha Kaverappa #define pldm_msgbuf_copy(dst, src, type, name)                                 \
1045909bf7c2SVarsha Kaverappa 	pldm__msgbuf_copy(dst, src, sizeof(type), #name)
104676712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
1047909bf7c2SVarsha Kaverappa // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
104876712f69SAndrew Jeffery pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1049909bf7c2SVarsha Kaverappa 		  const char *description)
1050909bf7c2SVarsha Kaverappa {
1051*c8df31c1SAndrew Jeffery 	assert(src);
1052*c8df31c1SAndrew Jeffery 	assert(dst);
1053*c8df31c1SAndrew Jeffery 	assert(src->mode == dst->mode);
1054*c8df31c1SAndrew Jeffery 
1055*c8df31c1SAndrew Jeffery 	if (!src->cursor || !dst->cursor || !description) {
1056*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EINVAL);
1057909bf7c2SVarsha Kaverappa 	}
1058909bf7c2SVarsha Kaverappa 
1059909bf7c2SVarsha Kaverappa #if INTMAX_MAX < SIZE_MAX
1060909bf7c2SVarsha Kaverappa 	if (size > INTMAX_MAX) {
1061*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1062909bf7c2SVarsha Kaverappa 	}
1063909bf7c2SVarsha Kaverappa #endif
1064909bf7c2SVarsha Kaverappa 
1065909bf7c2SVarsha Kaverappa 	if (src->remaining < INTMAX_MIN + (intmax_t)size) {
1066*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1067909bf7c2SVarsha Kaverappa 	}
1068909bf7c2SVarsha Kaverappa 
1069909bf7c2SVarsha Kaverappa 	if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
1070*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1071909bf7c2SVarsha Kaverappa 	}
1072909bf7c2SVarsha Kaverappa 
1073909bf7c2SVarsha Kaverappa 	src->remaining -= (intmax_t)size;
1074909bf7c2SVarsha Kaverappa 	assert(src->remaining >= 0);
1075909bf7c2SVarsha Kaverappa 	if (src->remaining < 0) {
1076*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1077909bf7c2SVarsha Kaverappa 	}
1078909bf7c2SVarsha Kaverappa 
1079909bf7c2SVarsha Kaverappa 	dst->remaining -= (intmax_t)size;
1080909bf7c2SVarsha Kaverappa 	assert(dst->remaining >= 0);
1081909bf7c2SVarsha Kaverappa 	if (dst->remaining < 0) {
1082*c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1083909bf7c2SVarsha Kaverappa 	}
1084909bf7c2SVarsha Kaverappa 
1085909bf7c2SVarsha Kaverappa 	memcpy(dst->cursor, src->cursor, size);
1086909bf7c2SVarsha Kaverappa 	src->cursor += size;
1087909bf7c2SVarsha Kaverappa 	dst->cursor += size;
1088909bf7c2SVarsha Kaverappa 
1089*c8df31c1SAndrew Jeffery 	return 0;
1090909bf7c2SVarsha Kaverappa }
1091*c8df31c1SAndrew Jeffery 
1092c63f63a2SAndrew Jeffery #ifdef __cplusplus
1093c63f63a2SAndrew Jeffery }
1094c63f63a2SAndrew Jeffery #endif
1095c63f63a2SAndrew Jeffery 
109666c7723aSAndrew Jeffery #ifdef __cplusplus
109766c7723aSAndrew Jeffery #include <type_traits>
109866c7723aSAndrew Jeffery 
109966c7723aSAndrew Jeffery template <typename T>
110066c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
110166c7723aSAndrew Jeffery 						void *buf)
110266c7723aSAndrew Jeffery {
110366c7723aSAndrew Jeffery 	static_assert(std::is_same<uint8_t *, T>::value);
110466c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint8(ctx, buf);
110566c7723aSAndrew Jeffery }
110666c7723aSAndrew Jeffery 
110766c7723aSAndrew Jeffery template <typename T>
110866c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
110966c7723aSAndrew Jeffery 					       void *buf)
111066c7723aSAndrew Jeffery {
111166c7723aSAndrew Jeffery 	static_assert(std::is_same<int8_t *, T>::value);
111266c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int8(ctx, buf);
111366c7723aSAndrew Jeffery }
111466c7723aSAndrew Jeffery 
111566c7723aSAndrew Jeffery template <typename T>
111666c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
111766c7723aSAndrew Jeffery 						 void *buf)
111866c7723aSAndrew Jeffery {
111966c7723aSAndrew Jeffery 	static_assert(std::is_same<uint16_t *, T>::value);
112066c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint16(ctx, buf);
112166c7723aSAndrew Jeffery }
112266c7723aSAndrew Jeffery 
112366c7723aSAndrew Jeffery template <typename T>
112466c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
112566c7723aSAndrew Jeffery 						void *buf)
112666c7723aSAndrew Jeffery {
112766c7723aSAndrew Jeffery 	static_assert(std::is_same<int16_t *, T>::value);
112866c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int16(ctx, buf);
112966c7723aSAndrew Jeffery }
113066c7723aSAndrew Jeffery 
113166c7723aSAndrew Jeffery template <typename T>
113266c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
113366c7723aSAndrew Jeffery 						 void *buf)
113466c7723aSAndrew Jeffery {
113566c7723aSAndrew Jeffery 	static_assert(std::is_same<uint32_t *, T>::value);
113666c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint32(ctx, buf);
113766c7723aSAndrew Jeffery }
113866c7723aSAndrew Jeffery 
113966c7723aSAndrew Jeffery template <typename T>
114066c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
114166c7723aSAndrew Jeffery 						void *buf)
114266c7723aSAndrew Jeffery {
114366c7723aSAndrew Jeffery 	static_assert(std::is_same<int32_t *, T>::value);
114466c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int32(ctx, buf);
114566c7723aSAndrew Jeffery }
114666c7723aSAndrew Jeffery 
114766c7723aSAndrew Jeffery template <typename T>
114866c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
114966c7723aSAndrew Jeffery 						 void *buf)
115066c7723aSAndrew Jeffery {
115166c7723aSAndrew Jeffery 	static_assert(std::is_same<real32_t *, T>::value);
115266c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_real32(ctx, buf);
115366c7723aSAndrew Jeffery }
115466c7723aSAndrew Jeffery #endif
115566c7723aSAndrew Jeffery 
1156c63f63a2SAndrew Jeffery #endif /* BUF_H */
1157