xref: /openbmc/libpldm/src/msgbuf.h (revision 9e3a5d45)
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>
49c8df31c1SAndrew 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");
68c8df31c1SAndrew Jeffery 	static_assert(PLDM_SUCCESS == 0, "Error handling is broken");
692ff8cf89SAndrew Jeffery 	int compliance;
702ff8cf89SAndrew Jeffery } build_assertions __attribute__((unused));
712ff8cf89SAndrew Jeffery 
72c8df31c1SAndrew Jeffery enum pldm_msgbuf_error_mode {
73c8df31c1SAndrew Jeffery 	PLDM_MSGBUF_PLDM_CC = 0x5a,
74c8df31c1SAndrew Jeffery 	PLDM_MSGBUF_C_ERRNO = 0xa5,
75c8df31c1SAndrew Jeffery };
76c8df31c1SAndrew Jeffery 
77c63f63a2SAndrew Jeffery struct pldm_msgbuf {
78062c8762SThu Nguyen 	uint8_t *cursor;
792ff8cf89SAndrew Jeffery 	intmax_t remaining;
80c8df31c1SAndrew Jeffery 	enum pldm_msgbuf_error_mode mode;
81c63f63a2SAndrew Jeffery };
82c63f63a2SAndrew Jeffery 
83d861a681SAndrew Jeffery /**
84d861a681SAndrew Jeffery  * @brief Either negate an errno value or return a value mapped to a PLDM
85d861a681SAndrew Jeffery  * completion code.
86d861a681SAndrew Jeffery  *
87d861a681SAndrew Jeffery  * Note that `pldm_msgbuf_status()` is purely internal to the msgbuf API
88d861a681SAndrew Jeffery  * for ergonomics. It's preferred that we don't try to unify this with
89d861a681SAndrew Jeffery  * `pldm_xlate_errno()` from src/api.h despite the similarities.
90d861a681SAndrew Jeffery  *
91d861a681SAndrew Jeffery  * @param[in] ctx - The msgbuf context providing the personality info
92d861a681SAndrew Jeffery  * @param[in] err - The positive errno value to translate
93d861a681SAndrew Jeffery  *
94d861a681SAndrew Jeffery  * @return Either the negated value of @p err if the context's error mode is
95d861a681SAndrew Jeffery  *         `PLDM_MSGBUF_C_ERRNO`, or the equivalent PLDM completion code if the
96d861a681SAndrew Jeffery  *         error mode is `PLDM_MSGBUF_PLDM_CC`.
97d861a681SAndrew Jeffery  */
98c8df31c1SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_status(struct pldm_msgbuf * ctx,unsigned int err)99c8df31c1SAndrew Jeffery pldm_msgbuf_status(struct pldm_msgbuf *ctx, unsigned int err)
100c8df31c1SAndrew Jeffery {
101c8df31c1SAndrew Jeffery 	int rc;
102c8df31c1SAndrew Jeffery 
103c8df31c1SAndrew Jeffery 	assert(err != 0);
104c8df31c1SAndrew Jeffery 	assert(err <= INT_MAX);
105c8df31c1SAndrew Jeffery 
106c8df31c1SAndrew Jeffery 	if (ctx->mode == PLDM_MSGBUF_C_ERRNO) {
107c8df31c1SAndrew Jeffery 		if (err > INT_MAX) {
108c8df31c1SAndrew Jeffery 			return -EINVAL;
109c8df31c1SAndrew Jeffery 		}
110c8df31c1SAndrew Jeffery 
111c8df31c1SAndrew Jeffery 		static_assert(INT_MIN + INT_MAX < 0,
112c8df31c1SAndrew Jeffery 			      "Arithmetic assumption failure");
113c8df31c1SAndrew Jeffery 		return -((int)err);
114c8df31c1SAndrew Jeffery 	}
115c8df31c1SAndrew Jeffery 
116c8df31c1SAndrew Jeffery 	if (err > INT_MAX) {
117c8df31c1SAndrew Jeffery 		return PLDM_ERROR;
118c8df31c1SAndrew Jeffery 	}
119c8df31c1SAndrew Jeffery 
120c8df31c1SAndrew Jeffery 	assert(ctx->mode == PLDM_MSGBUF_PLDM_CC);
121c8df31c1SAndrew Jeffery 	switch (err) {
122c8df31c1SAndrew Jeffery 	case EINVAL:
123c8df31c1SAndrew Jeffery 		rc = PLDM_ERROR_INVALID_DATA;
124c8df31c1SAndrew Jeffery 		break;
125c8df31c1SAndrew Jeffery 	case EBADMSG:
126c8df31c1SAndrew Jeffery 	case EOVERFLOW:
127c8df31c1SAndrew Jeffery 		rc = PLDM_ERROR_INVALID_LENGTH;
128c8df31c1SAndrew Jeffery 		break;
129c8df31c1SAndrew Jeffery 	default:
130c8df31c1SAndrew Jeffery 		assert(false);
131c8df31c1SAndrew Jeffery 		rc = PLDM_ERROR;
132c8df31c1SAndrew Jeffery 		break;
133c8df31c1SAndrew Jeffery 	}
134c8df31c1SAndrew Jeffery 
135c8df31c1SAndrew Jeffery 	assert(rc > 0);
136c8df31c1SAndrew Jeffery 	return rc;
137c8df31c1SAndrew Jeffery }
138c8df31c1SAndrew Jeffery 
139c63f63a2SAndrew Jeffery /**
140c63f63a2SAndrew Jeffery  * @brief Initialize pldm buf struct for buf extractor
141c63f63a2SAndrew Jeffery  *
142c63f63a2SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
143c63f63a2SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
144c63f63a2SAndrew Jeffery  * @param[in] buf - buffer to be extracted
145c63f63a2SAndrew Jeffery  * @param[in] len - size of buffer
146c63f63a2SAndrew Jeffery  *
147c8df31c1SAndrew Jeffery  * @return 0 on success, otherwise an error code appropriate for the current
148c8df31c1SAndrew Jeffery  *         personality.
149c63f63a2SAndrew Jeffery  */
15076712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
151c8df31c1SAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_init(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)152c8df31c1SAndrew Jeffery pldm__msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
15376712f69SAndrew Jeffery 		  size_t len)
154c63f63a2SAndrew Jeffery {
155c8df31c1SAndrew Jeffery 	assert(ctx);
156c8df31c1SAndrew Jeffery 	assert(ctx->mode == PLDM_MSGBUF_PLDM_CC ||
157c8df31c1SAndrew Jeffery 	       ctx->mode == PLDM_MSGBUF_C_ERRNO);
158c8df31c1SAndrew Jeffery 
159c8df31c1SAndrew Jeffery 	if (!buf) {
160c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
161c63f63a2SAndrew Jeffery 	}
162c63f63a2SAndrew Jeffery 
1632ff8cf89SAndrew Jeffery 	if ((minsize > len)) {
164c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
165c63f63a2SAndrew Jeffery 	}
166c63f63a2SAndrew Jeffery 
1672ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX
1682ff8cf89SAndrew Jeffery 	if (len > INTMAX_MAX) {
169c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1702ff8cf89SAndrew Jeffery 	}
1712ff8cf89SAndrew Jeffery #endif
1722ff8cf89SAndrew Jeffery 
17307febdbbSAndrew Jeffery 	if ((uintptr_t)buf + len < len) {
174c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
175c63f63a2SAndrew Jeffery 	}
176c63f63a2SAndrew Jeffery 
177c63f63a2SAndrew Jeffery 	ctx->cursor = (uint8_t *)buf;
1782ff8cf89SAndrew Jeffery 	ctx->remaining = (intmax_t)len;
179c63f63a2SAndrew Jeffery 
180c8df31c1SAndrew Jeffery 	return 0;
181c8df31c1SAndrew Jeffery }
182c8df31c1SAndrew Jeffery 
183c8df31c1SAndrew Jeffery /**
184c8df31c1SAndrew Jeffery  * @brief Initialise a msgbuf instance to return errors as PLDM completion codes
185c8df31c1SAndrew Jeffery  *
186c8df31c1SAndrew Jeffery  * @see pldm__msgbuf_init
187c8df31c1SAndrew Jeffery  *
188c8df31c1SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
189c8df31c1SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
190c8df31c1SAndrew Jeffery  * @param[in] buf - buffer to be extracted
191c8df31c1SAndrew Jeffery  * @param[in] len - size of buffer
192c8df31c1SAndrew Jeffery  *
193c8df31c1SAndrew Jeffery  * @return PLDM_SUCCESS if the provided buffer region is sensible,
194c8df31c1SAndrew Jeffery  *         otherwise PLDM_ERROR_INVALID_DATA if pointer parameters are invalid,
195c8df31c1SAndrew Jeffery  *         or PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
196c8df31c1SAndrew Jeffery  */
197c8df31c1SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_init_cc(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)198c8df31c1SAndrew Jeffery pldm_msgbuf_init_cc(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
199c8df31c1SAndrew Jeffery 		    size_t len)
200c8df31c1SAndrew Jeffery {
201c8df31c1SAndrew Jeffery 	if (!ctx) {
202c8df31c1SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
203c8df31c1SAndrew Jeffery 	}
204c8df31c1SAndrew Jeffery 
205c8df31c1SAndrew Jeffery 	ctx->mode = PLDM_MSGBUF_PLDM_CC;
206c8df31c1SAndrew Jeffery 	return pldm__msgbuf_init(ctx, minsize, buf, len);
207c8df31c1SAndrew Jeffery }
208c8df31c1SAndrew Jeffery 
209c8df31c1SAndrew Jeffery /**
210c8df31c1SAndrew Jeffery  * @brief Initialise a msgbuf instance to return errors as negative errno values
211c8df31c1SAndrew Jeffery  *
212c8df31c1SAndrew Jeffery  * @see pldm__msgbuf_init
213c8df31c1SAndrew Jeffery  *
214c8df31c1SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
215c8df31c1SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
216c8df31c1SAndrew Jeffery  * @param[in] buf - buffer to be extracted
217c8df31c1SAndrew Jeffery  * @param[in] len - size of buffer
218c8df31c1SAndrew Jeffery  *
219c8df31c1SAndrew Jeffery  * @return 0 if the provided buffer region is sensible, otherwise -EINVAL if
220c8df31c1SAndrew Jeffery  *         pointer parameters are invalid, or -EOVERFLOW if length constraints
221c8df31c1SAndrew Jeffery  *         are violated.
222c8df31c1SAndrew Jeffery  */
223c8df31c1SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_init_errno(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)224c8df31c1SAndrew Jeffery pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
225c8df31c1SAndrew Jeffery 		       size_t len)
226c8df31c1SAndrew Jeffery {
227c8df31c1SAndrew Jeffery 	if (!ctx) {
228c8df31c1SAndrew Jeffery 		return -EINVAL;
229c8df31c1SAndrew Jeffery 	}
230c8df31c1SAndrew Jeffery 
231c8df31c1SAndrew Jeffery 	ctx->mode = PLDM_MSGBUF_C_ERRNO;
232c8df31c1SAndrew Jeffery 	return pldm__msgbuf_init(ctx, minsize, buf, len);
233c63f63a2SAndrew Jeffery }
234c63f63a2SAndrew Jeffery 
235c63f63a2SAndrew Jeffery /**
236c63f63a2SAndrew Jeffery  * @brief Validate buffer overflow state
237c63f63a2SAndrew Jeffery  *
238c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
239c63f63a2SAndrew Jeffery  *
240c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
241c63f63a2SAndrew Jeffery  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
242c63f63a2SAndrew Jeffery  * prior accesses would have occurred beyond the bounds of the buffer, and
243c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
244c63f63a2SAndrew Jeffery  * pointer.
245c63f63a2SAndrew Jeffery  */
24676712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_validate(struct pldm_msgbuf * ctx)24776712f69SAndrew Jeffery pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
248c63f63a2SAndrew Jeffery {
249c8df31c1SAndrew Jeffery 	assert(ctx);
250c8df31c1SAndrew Jeffery 	if (ctx->remaining < 0) {
251c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
252c63f63a2SAndrew Jeffery 	}
253c63f63a2SAndrew Jeffery 
254c8df31c1SAndrew Jeffery 	return 0;
255c63f63a2SAndrew Jeffery }
256c63f63a2SAndrew Jeffery 
257c63f63a2SAndrew Jeffery /**
258db7b8324SAndrew Jeffery  * @brief Test whether a message buffer has been exactly consumed
259db7b8324SAndrew Jeffery  *
260db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
261db7b8324SAndrew Jeffery  *
262db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
263db7b8324SAndrew Jeffery  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
264db7b8324SAndrew Jeffery  * indicates that an incorrect sequence of accesses have occurred, and
265db7b8324SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
266db7b8324SAndrew Jeffery  * pointer.
267db7b8324SAndrew Jeffery  */
26876712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_consumed(struct pldm_msgbuf * ctx)26976712f69SAndrew Jeffery pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
270db7b8324SAndrew Jeffery {
271c8df31c1SAndrew Jeffery 	assert(ctx);
272c8df31c1SAndrew Jeffery 	if (ctx->remaining != 0) {
273c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EBADMSG);
274db7b8324SAndrew Jeffery 	}
275db7b8324SAndrew Jeffery 
276c8df31c1SAndrew Jeffery 	return 0;
277db7b8324SAndrew Jeffery }
278db7b8324SAndrew Jeffery 
279db7b8324SAndrew Jeffery /**
280c63f63a2SAndrew Jeffery  * @brief Destroy the pldm buf
281c63f63a2SAndrew Jeffery  *
282c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
283c63f63a2SAndrew Jeffery  *
284c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
285c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
286c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
287c63f63a2SAndrew Jeffery  * bounds of the buffer.
288c63f63a2SAndrew Jeffery  */
28976712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_destroy(struct pldm_msgbuf * ctx)29076712f69SAndrew Jeffery pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
291c63f63a2SAndrew Jeffery {
292c63f63a2SAndrew Jeffery 	int valid;
293c63f63a2SAndrew Jeffery 
294c8df31c1SAndrew Jeffery 	assert(ctx);
295c63f63a2SAndrew Jeffery 	valid = pldm_msgbuf_validate(ctx);
296c63f63a2SAndrew Jeffery 
297c63f63a2SAndrew Jeffery 	ctx->cursor = NULL;
298c63f63a2SAndrew Jeffery 	ctx->remaining = 0;
299c63f63a2SAndrew Jeffery 
300c63f63a2SAndrew Jeffery 	return valid;
301c63f63a2SAndrew Jeffery }
302c63f63a2SAndrew Jeffery 
303c63f63a2SAndrew Jeffery /**
304db7b8324SAndrew Jeffery  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
305db7b8324SAndrew Jeffery  * has been completely consumed without overflow
306db7b8324SAndrew Jeffery  *
307db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context
308db7b8324SAndrew Jeffery  *
309db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
310db7b8324SAndrew Jeffery  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
311db7b8324SAndrew Jeffery  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
312db7b8324SAndrew Jeffery  * have occurred byond the bounds of the buffer
313db7b8324SAndrew Jeffery  */
31476712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_destroy_consumed(struct pldm_msgbuf * ctx)31576712f69SAndrew Jeffery pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
316db7b8324SAndrew Jeffery {
317db7b8324SAndrew Jeffery 	int consumed;
318db7b8324SAndrew Jeffery 
319c8df31c1SAndrew Jeffery 	assert(ctx);
320db7b8324SAndrew Jeffery 	consumed = pldm_msgbuf_consumed(ctx);
321db7b8324SAndrew Jeffery 
322db7b8324SAndrew Jeffery 	ctx->cursor = NULL;
323db7b8324SAndrew Jeffery 	ctx->remaining = 0;
324db7b8324SAndrew Jeffery 
325db7b8324SAndrew Jeffery 	return consumed;
326db7b8324SAndrew Jeffery }
327db7b8324SAndrew Jeffery 
32866c7723aSAndrew Jeffery /*
32966c7723aSAndrew Jeffery  * Exploit the pre-processor to perform type checking by macro substitution.
33066c7723aSAndrew Jeffery  *
33166c7723aSAndrew Jeffery  * A C type is defined by its alignment as well as its object
33266c7723aSAndrew Jeffery  * size, and compilers have a hammer to enforce it in the form of
33366c7723aSAndrew Jeffery  * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
33466c7723aSAndrew Jeffery  * the libpldm public API this presents a problem: Naively attempting to use the
33566c7723aSAndrew Jeffery  * msgbuf APIs on a member of a packed struct would yield an error.
33666c7723aSAndrew Jeffery  *
33766c7723aSAndrew Jeffery  * The msgbuf APIs are implemented such that data is moved through unaligned
33866c7723aSAndrew Jeffery  * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
33966c7723aSAndrew Jeffery  * make the object pointers take a trip through `void *` at its API boundary.
34066c7723aSAndrew Jeffery  * That presents a bit too much of an opportunity to non-surgically remove your
34166c7723aSAndrew Jeffery  * own foot, so here we set about doing something to mitigate that as well.
34266c7723aSAndrew Jeffery  *
34366c7723aSAndrew Jeffery  * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
34466c7723aSAndrew Jeffery  * only for the purpose of object sizes, disregarding alignment. We have a few
34566c7723aSAndrew Jeffery  * constraints that cause some headaches:
34666c7723aSAndrew Jeffery  *
34766c7723aSAndrew Jeffery  * 1. We have to perform the type-check before a call through a C function,
34866c7723aSAndrew Jeffery  *    as the function must take the object pointer argument as `void *`.
34966c7723aSAndrew Jeffery  *    Essentially, this constrains us to doing something with macros.
35066c7723aSAndrew Jeffery  *
35166c7723aSAndrew Jeffery  * 2. While libpldm is a C library, its test suite is written in C++ to take
35266c7723aSAndrew Jeffery  *    advantage of gtest.
35366c7723aSAndrew Jeffery  *
35466c7723aSAndrew Jeffery  * 3. Ideally we'd do something with C's `static_assert()`, however
35566c7723aSAndrew Jeffery  *    `static_assert()` is defined as void, and as we're constrained to macros,
35666c7723aSAndrew Jeffery  *    using `static_assert()` would require a statement-expression
35766c7723aSAndrew Jeffery  *
35866c7723aSAndrew Jeffery  * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
35966c7723aSAndrew Jeffery  *    are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
36066c7723aSAndrew Jeffery  *    the purpose of enabling statement-expressions in this one instance.
36166c7723aSAndrew Jeffery  *
36266c7723aSAndrew Jeffery  * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
36366c7723aSAndrew Jeffery  *    however it's implemented in terms of `_Generic()`, which is not available
36466c7723aSAndrew Jeffery  *    in C++.
36566c7723aSAndrew Jeffery  *
36666c7723aSAndrew Jeffery  * Combined this means we need separate solutions for C and C++.
36766c7723aSAndrew Jeffery  *
36866c7723aSAndrew Jeffery  * For C, as we don't have statement-expressions, we need to exploit some other
36966c7723aSAndrew Jeffery  * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
37066c7723aSAndrew Jeffery  * API function call. We also have to take care of the fact that the call-sites
37166c7723aSAndrew Jeffery  * may be in the context of a variable assignment for error-handling purposes.
37266c7723aSAndrew Jeffery  * The key observation is that we can use the comma operator as a sequence point
37366c7723aSAndrew Jeffery  * to order the type check before the API call, discarding the "result" value of
37466c7723aSAndrew Jeffery  * the type check and yielding the return value of the API call.
37566c7723aSAndrew Jeffery  *
37666c7723aSAndrew Jeffery  * C++ could be less of a headache than the C as we can leverage template
37766c7723aSAndrew Jeffery  * functions. An advantage of template functions is that while their definition
37866c7723aSAndrew Jeffery  * is driven by instantion, the definition does not appear at the source
379*9e3a5d45SManojkiran Eda  * location of the instantiation, which gives it a great leg-up over the problems
38066c7723aSAndrew Jeffery  * we have in the C path. However, the use of the msgbuf APIs in the test suite
38166c7723aSAndrew Jeffery  * still makes things somewhat tricky, as the call-sites in the test suite are
38266c7723aSAndrew Jeffery  * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
38366c7723aSAndrew Jeffery  * takes both the object type and the required type as template arguments, and
38466c7723aSAndrew Jeffery  * then define the object pointer parameter as `void *` for a call through to
38566c7723aSAndrew Jeffery  * the appropriate msgbuf API. However, because the msgbuf API call-sites are
38666c7723aSAndrew Jeffery  * encapsulated in gtest macros, use of commas in the template specification
38766c7723aSAndrew Jeffery  * causes pre-processor confusion. In this way we're constrained to only one
38866c7723aSAndrew Jeffery  * template argument per function.
38966c7723aSAndrew Jeffery  *
39066c7723aSAndrew Jeffery  * Implement the C++ path using template functions that take the destination
39166c7723aSAndrew Jeffery  * object type as a template argument, while the name of the function symbols
39266c7723aSAndrew Jeffery  * are derived from the required type. The manual implementations of these
39366c7723aSAndrew Jeffery  * appear at the end of the header. The type safety is actually enforced
39466c7723aSAndrew Jeffery  * by `static_assert()` this time, as we can use statements as we're not
39566c7723aSAndrew Jeffery  * constrained to an expression in the templated function body.
39666c7723aSAndrew Jeffery  *
39766c7723aSAndrew Jeffery  * The invocations of pldm_msgbuf_extract_typecheck() typically result in
39866c7723aSAndrew Jeffery  * double-evaluation of some arguments. We're not yet bothered by this for two
39966c7723aSAndrew Jeffery  * reasons:
40066c7723aSAndrew Jeffery  *
40166c7723aSAndrew Jeffery  * 1. The nature of the current call-sites are such that there are no
40266c7723aSAndrew Jeffery  *    argument expressions that result in undesirable side-effects
40366c7723aSAndrew Jeffery  *
40466c7723aSAndrew Jeffery  * 2. It's an API internal to the libpldm implementation, and we can fix things
40566c7723aSAndrew Jeffery  *    whenever something crops up the violates the observation in 1.
40666c7723aSAndrew Jeffery  */
40766c7723aSAndrew Jeffery #ifdef __cplusplus
40866c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
40966c7723aSAndrew Jeffery 	pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
41066c7723aSAndrew Jeffery #else
41166c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
41266c7723aSAndrew Jeffery 	(pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
41366c7723aSAndrew Jeffery #endif
41466c7723aSAndrew Jeffery 
415db7b8324SAndrew Jeffery /**
416c63f63a2SAndrew Jeffery  * @brief pldm_msgbuf extractor for a uint8_t
417c63f63a2SAndrew Jeffery  *
418*9e3a5d45SManojkiran Eda  * @param[in,out] ctx - pldm_msgbuf context for extractor
419c63f63a2SAndrew Jeffery  * @param[out] dst - destination of extracted value
420c63f63a2SAndrew Jeffery  *
421c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
422c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH otherwise.
423c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
424c63f63a2SAndrew Jeffery  */
42566c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint8(ctx, dst)                                    \
42666c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8,     \
42766c7723aSAndrew Jeffery 				      dst, ctx, dst)
42876712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
42966c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint8(struct pldm_msgbuf * ctx,void * dst)43076712f69SAndrew Jeffery pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
431c63f63a2SAndrew Jeffery {
432c8df31c1SAndrew Jeffery 	assert(ctx);
433c8df31c1SAndrew Jeffery 
434c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
435c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
436c63f63a2SAndrew Jeffery 	}
437c63f63a2SAndrew Jeffery 
4382ff8cf89SAndrew Jeffery 	if (ctx->remaining == INTMAX_MIN) {
4392ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
440c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
4412ff8cf89SAndrew Jeffery 	}
44266c7723aSAndrew Jeffery 	ctx->remaining -= sizeof(uint8_t);
443c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
444c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
445c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
446c63f63a2SAndrew Jeffery 	}
447c63f63a2SAndrew Jeffery 
44866c7723aSAndrew Jeffery 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
44966c7723aSAndrew Jeffery 
450c63f63a2SAndrew Jeffery 	ctx->cursor++;
451c8df31c1SAndrew Jeffery 	return 0;
452c63f63a2SAndrew Jeffery }
453c63f63a2SAndrew Jeffery 
45466c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
45566c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
45666c7723aSAndrew Jeffery 				      ctx, dst)
45776712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
45866c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int8(struct pldm_msgbuf * ctx,void * dst)45976712f69SAndrew Jeffery pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
460c63f63a2SAndrew Jeffery {
461c8df31c1SAndrew Jeffery 	assert(ctx);
462c8df31c1SAndrew Jeffery 
463c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
464c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
465c63f63a2SAndrew Jeffery 	}
466c63f63a2SAndrew Jeffery 
4672ff8cf89SAndrew Jeffery 	if (ctx->remaining == INTMAX_MIN) {
4682ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
469c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
4702ff8cf89SAndrew Jeffery 	}
47166c7723aSAndrew Jeffery 	ctx->remaining -= sizeof(int8_t);
472c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
473c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
474c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
475c63f63a2SAndrew Jeffery 	}
476c63f63a2SAndrew Jeffery 
47766c7723aSAndrew Jeffery 	memcpy(dst, ctx->cursor, sizeof(int8_t));
478c63f63a2SAndrew Jeffery 	ctx->cursor++;
479c8df31c1SAndrew Jeffery 	return 0;
480c63f63a2SAndrew Jeffery }
481c63f63a2SAndrew Jeffery 
48266c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
48366c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
48466c7723aSAndrew Jeffery 				      dst, ctx, dst)
48576712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
48666c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint16(struct pldm_msgbuf * ctx,void * dst)48776712f69SAndrew Jeffery pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst)
488c63f63a2SAndrew Jeffery {
489c63f63a2SAndrew Jeffery 	uint16_t ldst;
490c63f63a2SAndrew Jeffery 
491c8df31c1SAndrew Jeffery 	assert(ctx);
492c8df31c1SAndrew Jeffery 
493c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
494c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
495c63f63a2SAndrew Jeffery 	}
496c63f63a2SAndrew Jeffery 
4972ff8cf89SAndrew Jeffery 	// Check for underflow while tracking the magnitude of the buffer overflow
4982ff8cf89SAndrew Jeffery 	static_assert(
4992ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
5002ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
5012ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
5022ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
5032ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
504c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
5052ff8cf89SAndrew Jeffery 	}
5062ff8cf89SAndrew Jeffery 
507c63f63a2SAndrew Jeffery 	// Check for buffer overflow. If we overflow, account for the request as
508c63f63a2SAndrew Jeffery 	// negative values in ctx->remaining. This way we can debug how far
509c63f63a2SAndrew Jeffery 	// we've overflowed.
510c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
511c63f63a2SAndrew Jeffery 
512c63f63a2SAndrew Jeffery 	// Prevent the access if it would overflow. First, assert so we blow up
513c63f63a2SAndrew Jeffery 	// the test suite right at the point of failure. However, cater to
514c63f63a2SAndrew Jeffery 	// -DNDEBUG by explicitly testing that the access is valid.
515c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
516c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
517c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
518c63f63a2SAndrew Jeffery 	}
519c63f63a2SAndrew Jeffery 
520c63f63a2SAndrew Jeffery 	// Use memcpy() to have the compiler deal with any alignment
521c63f63a2SAndrew Jeffery 	// issues on the target architecture
522c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
523c63f63a2SAndrew Jeffery 
524c63f63a2SAndrew Jeffery 	// Only assign the target value once it's correctly decoded
52566c7723aSAndrew Jeffery 	ldst = le16toh(ldst);
52666c7723aSAndrew Jeffery 
52766c7723aSAndrew Jeffery 	// Allow storing to unaligned
52866c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
52966c7723aSAndrew Jeffery 
530c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
531c63f63a2SAndrew Jeffery 
532c8df31c1SAndrew Jeffery 	return 0;
533c63f63a2SAndrew Jeffery }
534c63f63a2SAndrew Jeffery 
53566c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
53666c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
53766c7723aSAndrew Jeffery 				      dst, ctx, dst)
53876712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
53966c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int16(struct pldm_msgbuf * ctx,void * dst)54076712f69SAndrew Jeffery pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
541c63f63a2SAndrew Jeffery {
542c63f63a2SAndrew Jeffery 	int16_t ldst;
543c63f63a2SAndrew Jeffery 
544c8df31c1SAndrew Jeffery 	assert(ctx);
545c8df31c1SAndrew Jeffery 
546c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
547c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
548c63f63a2SAndrew Jeffery 	}
549c63f63a2SAndrew Jeffery 
5502ff8cf89SAndrew Jeffery 	static_assert(
5512ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
5522ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
5532ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
5542ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
5552ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
556c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
5572ff8cf89SAndrew Jeffery 	}
558c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
559c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
560c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
561c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
562c63f63a2SAndrew Jeffery 	}
563c63f63a2SAndrew Jeffery 
564c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
565c63f63a2SAndrew Jeffery 
56666c7723aSAndrew Jeffery 	ldst = le16toh(ldst);
56766c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
568c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
569c63f63a2SAndrew Jeffery 
570c8df31c1SAndrew Jeffery 	return 0;
571c63f63a2SAndrew Jeffery }
572c63f63a2SAndrew Jeffery 
57366c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
57466c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
57566c7723aSAndrew Jeffery 				      dst, ctx, dst)
57676712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
57766c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint32(struct pldm_msgbuf * ctx,void * dst)57876712f69SAndrew Jeffery pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst)
579c63f63a2SAndrew Jeffery {
580c63f63a2SAndrew Jeffery 	uint32_t ldst;
581c63f63a2SAndrew Jeffery 
582c8df31c1SAndrew Jeffery 	assert(ctx);
583c8df31c1SAndrew Jeffery 
584c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
585c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
586c63f63a2SAndrew Jeffery 	}
587c63f63a2SAndrew Jeffery 
5882ff8cf89SAndrew Jeffery 	static_assert(
5892ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
5902ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
5912ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
5922ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
5932ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
594c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
5952ff8cf89SAndrew Jeffery 	}
596c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
597c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
598c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
599c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
600c63f63a2SAndrew Jeffery 	}
601c63f63a2SAndrew Jeffery 
602c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
60366c7723aSAndrew Jeffery 	ldst = le32toh(ldst);
60466c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
605c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
606c63f63a2SAndrew Jeffery 
607c8df31c1SAndrew Jeffery 	return 0;
608c63f63a2SAndrew Jeffery }
609c63f63a2SAndrew Jeffery 
61066c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
61166c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
61266c7723aSAndrew Jeffery 				      dst, ctx, dst)
61376712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
61466c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int32(struct pldm_msgbuf * ctx,void * dst)61576712f69SAndrew Jeffery pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
616c63f63a2SAndrew Jeffery {
617c63f63a2SAndrew Jeffery 	int32_t ldst;
618c63f63a2SAndrew Jeffery 
619c8df31c1SAndrew Jeffery 	assert(ctx);
620c8df31c1SAndrew Jeffery 
621c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
622c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
623c63f63a2SAndrew Jeffery 	}
624c63f63a2SAndrew Jeffery 
6252ff8cf89SAndrew Jeffery 	static_assert(
6262ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
6272ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
6282ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
6292ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
6302ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
631c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
6322ff8cf89SAndrew Jeffery 	}
633c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
634c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
635c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
636c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
637c63f63a2SAndrew Jeffery 	}
638c63f63a2SAndrew Jeffery 
639c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
64066c7723aSAndrew Jeffery 	ldst = le32toh(ldst);
64166c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
642c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
643c63f63a2SAndrew Jeffery 
644c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
645c63f63a2SAndrew Jeffery }
646c63f63a2SAndrew Jeffery 
64766c7723aSAndrew Jeffery #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
64866c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
64966c7723aSAndrew Jeffery 				      dst, ctx, dst)
65076712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
65166c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_real32(struct pldm_msgbuf * ctx,void * dst)65276712f69SAndrew Jeffery pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst)
653c63f63a2SAndrew Jeffery {
654c63f63a2SAndrew Jeffery 	uint32_t ldst;
655c63f63a2SAndrew Jeffery 
656c8df31c1SAndrew Jeffery 	static_assert(sizeof(real32_t) == sizeof(ldst),
65766c7723aSAndrew Jeffery 		      "Mismatched type sizes for dst and ldst");
65866c7723aSAndrew Jeffery 
659c8df31c1SAndrew Jeffery 	assert(ctx);
660c8df31c1SAndrew Jeffery 
661c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
662c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
663c63f63a2SAndrew Jeffery 	}
664c63f63a2SAndrew Jeffery 
6652ff8cf89SAndrew Jeffery 	static_assert(
6662ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
6672ff8cf89SAndrew Jeffery 		sizeof(ldst) < INTMAX_MAX,
6682ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
6692ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
6702ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
671c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
6722ff8cf89SAndrew Jeffery 	}
673c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
674c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
675c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
676c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
677c63f63a2SAndrew Jeffery 	}
678c63f63a2SAndrew Jeffery 
679c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
680c63f63a2SAndrew Jeffery 	ldst = le32toh(ldst);
68166c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
68266c7723aSAndrew Jeffery 	ctx->cursor += sizeof(ldst);
683c63f63a2SAndrew Jeffery 
684c8df31c1SAndrew Jeffery 	return 0;
685c63f63a2SAndrew Jeffery }
686c63f63a2SAndrew Jeffery 
68766c7723aSAndrew Jeffery /**
68866c7723aSAndrew Jeffery  * Extract the field at the msgbuf cursor into the lvalue named by dst.
68966c7723aSAndrew Jeffery  *
69066c7723aSAndrew Jeffery  * @param ctx The msgbuf context object
69166c7723aSAndrew Jeffery  * @param dst The lvalue into which the field at the msgbuf cursor should be
69266c7723aSAndrew Jeffery  *            extracted
69366c7723aSAndrew Jeffery  *
69466c7723aSAndrew Jeffery  * @return PLDM_SUCCESS on success, otherwise another value on error
69566c7723aSAndrew Jeffery  */
696c63f63a2SAndrew Jeffery #define pldm_msgbuf_extract(ctx, dst)                                          \
69766c7723aSAndrew Jeffery 	_Generic((dst),                                                        \
69866c7723aSAndrew Jeffery 		uint8_t: pldm__msgbuf_extract_uint8,                           \
69966c7723aSAndrew Jeffery 		int8_t: pldm__msgbuf_extract_int8,                             \
70066c7723aSAndrew Jeffery 		uint16_t: pldm__msgbuf_extract_uint16,                         \
70166c7723aSAndrew Jeffery 		int16_t: pldm__msgbuf_extract_int16,                           \
70266c7723aSAndrew Jeffery 		uint32_t: pldm__msgbuf_extract_uint32,                         \
70366c7723aSAndrew Jeffery 		int32_t: pldm__msgbuf_extract_int32,                           \
70466c7723aSAndrew Jeffery 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
70566c7723aSAndrew Jeffery 
70666c7723aSAndrew Jeffery /**
70766c7723aSAndrew Jeffery  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
70866c7723aSAndrew Jeffery  *
70966c7723aSAndrew Jeffery  * @param ctx The msgbuf context object
71066c7723aSAndrew Jeffery  * @param dst The pointer to the object into which the field at the msgbuf
71166c7723aSAndrew Jeffery  *            cursor should be extracted
71266c7723aSAndrew Jeffery  *
71366c7723aSAndrew Jeffery  * @return PLDM_SUCCESS on success, otherwise another value on error
71466c7723aSAndrew Jeffery  */
71566c7723aSAndrew Jeffery #define pldm_msgbuf_extract_p(ctx, dst)                                        \
71666c7723aSAndrew Jeffery 	_Generic((dst),                                                        \
71766c7723aSAndrew Jeffery 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
71866c7723aSAndrew Jeffery 		int8_t *: pldm__msgbuf_extract_int8,                           \
71966c7723aSAndrew Jeffery 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
72066c7723aSAndrew Jeffery 		int16_t *: pldm__msgbuf_extract_int16,                         \
72166c7723aSAndrew Jeffery 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
72266c7723aSAndrew Jeffery 		int32_t *: pldm__msgbuf_extract_int32,                         \
72366c7723aSAndrew Jeffery 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
724c63f63a2SAndrew Jeffery 
72576712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf * ctx,uint8_t * dst,size_t count)72676712f69SAndrew Jeffery pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, uint8_t *dst,
72776712f69SAndrew Jeffery 				size_t count)
728369b121aSAndrew Jeffery {
729c8df31c1SAndrew Jeffery 	assert(ctx);
730c8df31c1SAndrew Jeffery 
731c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !dst) {
732c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
733369b121aSAndrew Jeffery 	}
734369b121aSAndrew Jeffery 
735369b121aSAndrew Jeffery 	if (!count) {
736c8df31c1SAndrew Jeffery 		return 0;
737369b121aSAndrew Jeffery 	}
738369b121aSAndrew Jeffery 
7392ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX
7402ff8cf89SAndrew Jeffery 	if (count > INTMAX_MAX) {
741c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
742369b121aSAndrew Jeffery 	}
7432ff8cf89SAndrew Jeffery #endif
744369b121aSAndrew Jeffery 
7452ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
746c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
7472ff8cf89SAndrew Jeffery 	}
7482ff8cf89SAndrew Jeffery 	ctx->remaining -= (intmax_t)count;
749369b121aSAndrew Jeffery 	assert(ctx->remaining >= 0);
750369b121aSAndrew Jeffery 	if (ctx->remaining < 0) {
751c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
752369b121aSAndrew Jeffery 	}
753369b121aSAndrew Jeffery 
754a065eccbSAndrew Jeffery 	memcpy(dst, ctx->cursor, count);
755a065eccbSAndrew Jeffery 	ctx->cursor += count;
756369b121aSAndrew Jeffery 
757c8df31c1SAndrew Jeffery 	return 0;
758369b121aSAndrew Jeffery }
759369b121aSAndrew Jeffery 
760369b121aSAndrew Jeffery #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
76137dd6a3dSAndrew Jeffery 	_Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
76237dd6a3dSAndrew Jeffery 								     count)
763369b121aSAndrew Jeffery 
76476712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_uint32(struct pldm_msgbuf * ctx,const uint32_t src)76576712f69SAndrew Jeffery pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, const uint32_t src)
766062c8762SThu Nguyen {
767062c8762SThu Nguyen 	uint32_t val = htole32(src);
768062c8762SThu Nguyen 
769c8df31c1SAndrew Jeffery 	assert(ctx);
770c8df31c1SAndrew Jeffery 
771c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
772c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
773062c8762SThu Nguyen 	}
774062c8762SThu Nguyen 
7752ff8cf89SAndrew Jeffery 	static_assert(
7762ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
7772ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
7782ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
7792ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
7802ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
781c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
7822ff8cf89SAndrew Jeffery 	}
783062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
784062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
785062c8762SThu Nguyen 	if (ctx->remaining < 0) {
786c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
787062c8762SThu Nguyen 	}
788062c8762SThu Nguyen 
789062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
790062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
791062c8762SThu Nguyen 
792c8df31c1SAndrew Jeffery 	return 0;
793062c8762SThu Nguyen }
794062c8762SThu Nguyen 
79576712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_uint16(struct pldm_msgbuf * ctx,const uint16_t src)79676712f69SAndrew Jeffery pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, const uint16_t src)
797062c8762SThu Nguyen {
798062c8762SThu Nguyen 	uint16_t val = htole16(src);
799062c8762SThu Nguyen 
800c8df31c1SAndrew Jeffery 	assert(ctx);
801c8df31c1SAndrew Jeffery 
802c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
803c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
804062c8762SThu Nguyen 	}
805062c8762SThu Nguyen 
8062ff8cf89SAndrew Jeffery 	static_assert(
8072ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
8082ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
8092ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
8102ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
8112ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
812c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
8132ff8cf89SAndrew Jeffery 	}
814062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
815062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
816062c8762SThu Nguyen 	if (ctx->remaining < 0) {
817c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
818062c8762SThu Nguyen 	}
819062c8762SThu Nguyen 
820062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
821062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
822062c8762SThu Nguyen 
823c8df31c1SAndrew Jeffery 	return 0;
824062c8762SThu Nguyen }
825062c8762SThu Nguyen 
82676712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_uint8(struct pldm_msgbuf * ctx,const uint8_t src)82776712f69SAndrew Jeffery pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, const uint8_t src)
828062c8762SThu Nguyen {
829c8df31c1SAndrew Jeffery 	assert(ctx);
830c8df31c1SAndrew Jeffery 
831c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
832c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
833062c8762SThu Nguyen 	}
834062c8762SThu Nguyen 
8352ff8cf89SAndrew Jeffery 	static_assert(
8362ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
8372ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
8382ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
8392ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
8402ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
841c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
8422ff8cf89SAndrew Jeffery 	}
843062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
844062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
845062c8762SThu Nguyen 	if (ctx->remaining < 0) {
846c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
847062c8762SThu Nguyen 	}
848062c8762SThu Nguyen 
849062c8762SThu Nguyen 	memcpy(ctx->cursor, &src, sizeof(src));
850062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
851062c8762SThu Nguyen 
852c8df31c1SAndrew Jeffery 	return 0;
853062c8762SThu Nguyen }
854062c8762SThu Nguyen 
85576712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_int32(struct pldm_msgbuf * ctx,const int32_t src)85676712f69SAndrew Jeffery pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, const int32_t src)
857062c8762SThu Nguyen {
858062c8762SThu Nguyen 	int32_t val = htole32(src);
859062c8762SThu Nguyen 
860c8df31c1SAndrew Jeffery 	assert(ctx);
861c8df31c1SAndrew Jeffery 
862c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
863c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
864062c8762SThu Nguyen 	}
865062c8762SThu Nguyen 
8662ff8cf89SAndrew Jeffery 	static_assert(
8672ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
8682ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
8692ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
8702ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
8712ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
872c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
8732ff8cf89SAndrew Jeffery 	}
874062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
875062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
876062c8762SThu Nguyen 	if (ctx->remaining < 0) {
877c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
878062c8762SThu Nguyen 	}
879062c8762SThu Nguyen 
880062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
881062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
882062c8762SThu Nguyen 
883c8df31c1SAndrew Jeffery 	return 0;
884062c8762SThu Nguyen }
885062c8762SThu Nguyen 
88676712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_int16(struct pldm_msgbuf * ctx,const int16_t src)88776712f69SAndrew Jeffery pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, const int16_t src)
888062c8762SThu Nguyen {
889062c8762SThu Nguyen 	int16_t val = htole16(src);
890062c8762SThu Nguyen 
891c8df31c1SAndrew Jeffery 	assert(ctx);
892c8df31c1SAndrew Jeffery 
893c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
894c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
895062c8762SThu Nguyen 	}
896062c8762SThu Nguyen 
8972ff8cf89SAndrew Jeffery 	static_assert(
8982ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
8992ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
9002ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
9012ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
9022ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
903c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
9042ff8cf89SAndrew Jeffery 	}
905062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
906062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
907062c8762SThu Nguyen 	if (ctx->remaining < 0) {
908c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
909062c8762SThu Nguyen 	}
910062c8762SThu Nguyen 
911062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
912062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
913062c8762SThu Nguyen 
914c8df31c1SAndrew Jeffery 	return 0;
915062c8762SThu Nguyen }
916062c8762SThu Nguyen 
91776712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_int8(struct pldm_msgbuf * ctx,const int8_t src)91876712f69SAndrew Jeffery pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, const int8_t src)
919062c8762SThu Nguyen {
920c8df31c1SAndrew Jeffery 	assert(ctx);
921c8df31c1SAndrew Jeffery 
922c8df31c1SAndrew Jeffery 	if (!ctx->cursor) {
923c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
924062c8762SThu Nguyen 	}
925062c8762SThu Nguyen 
9262ff8cf89SAndrew Jeffery 	static_assert(
9272ff8cf89SAndrew Jeffery 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
9282ff8cf89SAndrew Jeffery 		sizeof(src) < INTMAX_MAX,
9292ff8cf89SAndrew Jeffery 		"The following addition may not uphold the runtime assertion");
9302ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
9312ff8cf89SAndrew Jeffery 		assert(ctx->remaining < 0);
932c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
9332ff8cf89SAndrew Jeffery 	}
934062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
935062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
936062c8762SThu Nguyen 	if (ctx->remaining < 0) {
937c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
938062c8762SThu Nguyen 	}
939062c8762SThu Nguyen 
940062c8762SThu Nguyen 	memcpy(ctx->cursor, &src, sizeof(src));
941062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
942062c8762SThu Nguyen 
943c8df31c1SAndrew Jeffery 	return 0;
944062c8762SThu Nguyen }
945062c8762SThu Nguyen 
946062c8762SThu Nguyen #define pldm_msgbuf_insert(dst, src)                                           \
94737dd6a3dSAndrew Jeffery 	_Generic((src),                                                        \
94837dd6a3dSAndrew Jeffery 		uint8_t: pldm_msgbuf_insert_uint8,                             \
94937dd6a3dSAndrew Jeffery 		int8_t: pldm_msgbuf_insert_int8,                               \
95037dd6a3dSAndrew Jeffery 		uint16_t: pldm_msgbuf_insert_uint16,                           \
95137dd6a3dSAndrew Jeffery 		int16_t: pldm_msgbuf_insert_int16,                             \
95237dd6a3dSAndrew Jeffery 		uint32_t: pldm_msgbuf_insert_uint32,                           \
95337dd6a3dSAndrew Jeffery 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
954062c8762SThu Nguyen 
95576712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf * ctx,const uint8_t * src,size_t count)95676712f69SAndrew Jeffery pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, const uint8_t *src,
957062c8762SThu Nguyen 			       size_t count)
958062c8762SThu Nguyen {
959c8df31c1SAndrew Jeffery 	assert(ctx);
960c8df31c1SAndrew Jeffery 
961c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !src) {
962c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
963062c8762SThu Nguyen 	}
964062c8762SThu Nguyen 
965062c8762SThu Nguyen 	if (!count) {
966c8df31c1SAndrew Jeffery 		return 0;
967062c8762SThu Nguyen 	}
968062c8762SThu Nguyen 
9692ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX
9702ff8cf89SAndrew Jeffery 	if (count > INTMAX_MAX) {
971c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
972062c8762SThu Nguyen 	}
9732ff8cf89SAndrew Jeffery #endif
974062c8762SThu Nguyen 
9752ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
976c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
9772ff8cf89SAndrew Jeffery 	}
9782ff8cf89SAndrew Jeffery 	ctx->remaining -= (intmax_t)count;
979062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
980062c8762SThu Nguyen 	if (ctx->remaining < 0) {
981c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
982062c8762SThu Nguyen 	}
983062c8762SThu Nguyen 
984a065eccbSAndrew Jeffery 	memcpy(ctx->cursor, src, count);
985a065eccbSAndrew Jeffery 	ctx->cursor += count;
986062c8762SThu Nguyen 
987c8df31c1SAndrew Jeffery 	return 0;
988062c8762SThu Nguyen }
989062c8762SThu Nguyen 
990062c8762SThu Nguyen #define pldm_msgbuf_insert_array(dst, src, count)                              \
99137dd6a3dSAndrew Jeffery 	_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src,  \
99237dd6a3dSAndrew Jeffery 								    count)
993062c8762SThu Nguyen 
99476712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_span_required(struct pldm_msgbuf * ctx,size_t required,void ** cursor)99576712f69SAndrew Jeffery pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, size_t required,
99676712f69SAndrew Jeffery 			  void **cursor)
997062c8762SThu Nguyen {
998c8df31c1SAndrew Jeffery 	assert(ctx);
999c8df31c1SAndrew Jeffery 
1000c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !cursor || *cursor) {
1001c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
1002062c8762SThu Nguyen 	}
1003062c8762SThu Nguyen 
10042ff8cf89SAndrew Jeffery #if INTMAX_MAX < SIZE_MAX
10052ff8cf89SAndrew Jeffery 	if (required > INTMAX_MAX) {
1006c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1007062c8762SThu Nguyen 	}
10082ff8cf89SAndrew Jeffery #endif
1009062c8762SThu Nguyen 
10102ff8cf89SAndrew Jeffery 	if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
1011c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
10122ff8cf89SAndrew Jeffery 	}
10132ff8cf89SAndrew Jeffery 	ctx->remaining -= (intmax_t)required;
1014062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
1015062c8762SThu Nguyen 	if (ctx->remaining < 0) {
1016c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1017062c8762SThu Nguyen 	}
1018062c8762SThu Nguyen 
1019062c8762SThu Nguyen 	*cursor = ctx->cursor;
1020062c8762SThu Nguyen 	ctx->cursor += required;
1021062c8762SThu Nguyen 
1022c8df31c1SAndrew Jeffery 	return 0;
1023062c8762SThu Nguyen }
1024062c8762SThu Nguyen 
102576712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
pldm_msgbuf_span_remaining(struct pldm_msgbuf * ctx,void ** cursor,size_t * len)102676712f69SAndrew Jeffery pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
1027062c8762SThu Nguyen {
1028c8df31c1SAndrew Jeffery 	assert(ctx);
1029c8df31c1SAndrew Jeffery 
1030c8df31c1SAndrew Jeffery 	if (!ctx->cursor || !cursor || *cursor || !len) {
1031c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EINVAL);
1032062c8762SThu Nguyen 	}
1033062c8762SThu Nguyen 
1034062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
1035062c8762SThu Nguyen 	if (ctx->remaining < 0) {
1036c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1037062c8762SThu Nguyen 	}
1038062c8762SThu Nguyen 
1039062c8762SThu Nguyen 	*cursor = ctx->cursor;
1040062c8762SThu Nguyen 	ctx->cursor += ctx->remaining;
1041062c8762SThu Nguyen 	*len = ctx->remaining;
1042062c8762SThu Nguyen 	ctx->remaining = 0;
1043062c8762SThu Nguyen 
1044c8df31c1SAndrew Jeffery 	return 0;
1045062c8762SThu Nguyen }
1046909bf7c2SVarsha Kaverappa 
1047909bf7c2SVarsha Kaverappa /**
1048909bf7c2SVarsha Kaverappa  * @brief pldm_msgbuf copy data between two msg buffers
1049909bf7c2SVarsha Kaverappa  *
1050*9e3a5d45SManojkiran Eda  * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1051*9e3a5d45SManojkiran Eda  * @param[in,out] dst - destination of copy from source
1052909bf7c2SVarsha Kaverappa  * @param[in] size - size of data to be copied
1053909bf7c2SVarsha Kaverappa  * @param[in] description - description of data copied
1054909bf7c2SVarsha Kaverappa  *
1055909bf7c2SVarsha Kaverappa  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1056909bf7c2SVarsha Kaverappa  * PLDM_ERROR_INVALID_LENGTH otherwise.
1057909bf7c2SVarsha Kaverappa  * PLDM_ERROR_INVALID_DATA if input is invalid
1058909bf7c2SVarsha Kaverappa  */
1059909bf7c2SVarsha Kaverappa #define pldm_msgbuf_copy(dst, src, type, name)                                 \
1060909bf7c2SVarsha Kaverappa 	pldm__msgbuf_copy(dst, src, sizeof(type), #name)
106176712f69SAndrew Jeffery __attribute__((always_inline)) static inline int
1062909bf7c2SVarsha Kaverappa // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_copy(struct pldm_msgbuf * dst,struct pldm_msgbuf * src,size_t size,const char * description)106376712f69SAndrew Jeffery pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1064909bf7c2SVarsha Kaverappa 		  const char *description)
1065909bf7c2SVarsha Kaverappa {
1066c8df31c1SAndrew Jeffery 	assert(src);
1067c8df31c1SAndrew Jeffery 	assert(dst);
1068c8df31c1SAndrew Jeffery 	assert(src->mode == dst->mode);
1069c8df31c1SAndrew Jeffery 
1070c8df31c1SAndrew Jeffery 	if (!src->cursor || !dst->cursor || !description) {
1071c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EINVAL);
1072909bf7c2SVarsha Kaverappa 	}
1073909bf7c2SVarsha Kaverappa 
1074909bf7c2SVarsha Kaverappa #if INTMAX_MAX < SIZE_MAX
1075909bf7c2SVarsha Kaverappa 	if (size > INTMAX_MAX) {
1076c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1077909bf7c2SVarsha Kaverappa 	}
1078909bf7c2SVarsha Kaverappa #endif
1079909bf7c2SVarsha Kaverappa 
1080909bf7c2SVarsha Kaverappa 	if (src->remaining < INTMAX_MIN + (intmax_t)size) {
1081c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1082909bf7c2SVarsha Kaverappa 	}
1083909bf7c2SVarsha Kaverappa 
1084909bf7c2SVarsha Kaverappa 	if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
1085c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1086909bf7c2SVarsha Kaverappa 	}
1087909bf7c2SVarsha Kaverappa 
1088909bf7c2SVarsha Kaverappa 	src->remaining -= (intmax_t)size;
1089909bf7c2SVarsha Kaverappa 	assert(src->remaining >= 0);
1090909bf7c2SVarsha Kaverappa 	if (src->remaining < 0) {
1091c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1092909bf7c2SVarsha Kaverappa 	}
1093909bf7c2SVarsha Kaverappa 
1094909bf7c2SVarsha Kaverappa 	dst->remaining -= (intmax_t)size;
1095909bf7c2SVarsha Kaverappa 	assert(dst->remaining >= 0);
1096909bf7c2SVarsha Kaverappa 	if (dst->remaining < 0) {
1097c8df31c1SAndrew Jeffery 		return pldm_msgbuf_status(dst, EOVERFLOW);
1098909bf7c2SVarsha Kaverappa 	}
1099909bf7c2SVarsha Kaverappa 
1100909bf7c2SVarsha Kaverappa 	memcpy(dst->cursor, src->cursor, size);
1101909bf7c2SVarsha Kaverappa 	src->cursor += size;
1102909bf7c2SVarsha Kaverappa 	dst->cursor += size;
1103909bf7c2SVarsha Kaverappa 
1104c8df31c1SAndrew Jeffery 	return 0;
1105909bf7c2SVarsha Kaverappa }
1106c8df31c1SAndrew Jeffery 
1107c63f63a2SAndrew Jeffery #ifdef __cplusplus
1108c63f63a2SAndrew Jeffery }
1109c63f63a2SAndrew Jeffery #endif
1110c63f63a2SAndrew Jeffery 
111166c7723aSAndrew Jeffery #ifdef __cplusplus
111266c7723aSAndrew Jeffery #include <type_traits>
111366c7723aSAndrew Jeffery 
111466c7723aSAndrew Jeffery template <typename T>
pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf * ctx,void * buf)111566c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
111666c7723aSAndrew Jeffery 						void *buf)
111766c7723aSAndrew Jeffery {
111866c7723aSAndrew Jeffery 	static_assert(std::is_same<uint8_t *, T>::value);
111966c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint8(ctx, buf);
112066c7723aSAndrew Jeffery }
112166c7723aSAndrew Jeffery 
112266c7723aSAndrew Jeffery template <typename T>
pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf * ctx,void * buf)112366c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
112466c7723aSAndrew Jeffery 					       void *buf)
112566c7723aSAndrew Jeffery {
112666c7723aSAndrew Jeffery 	static_assert(std::is_same<int8_t *, T>::value);
112766c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int8(ctx, buf);
112866c7723aSAndrew Jeffery }
112966c7723aSAndrew Jeffery 
113066c7723aSAndrew Jeffery template <typename T>
pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf * ctx,void * buf)113166c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
113266c7723aSAndrew Jeffery 						 void *buf)
113366c7723aSAndrew Jeffery {
113466c7723aSAndrew Jeffery 	static_assert(std::is_same<uint16_t *, T>::value);
113566c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint16(ctx, buf);
113666c7723aSAndrew Jeffery }
113766c7723aSAndrew Jeffery 
113866c7723aSAndrew Jeffery template <typename T>
pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf * ctx,void * buf)113966c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
114066c7723aSAndrew Jeffery 						void *buf)
114166c7723aSAndrew Jeffery {
114266c7723aSAndrew Jeffery 	static_assert(std::is_same<int16_t *, T>::value);
114366c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int16(ctx, buf);
114466c7723aSAndrew Jeffery }
114566c7723aSAndrew Jeffery 
114666c7723aSAndrew Jeffery template <typename T>
pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf * ctx,void * buf)114766c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
114866c7723aSAndrew Jeffery 						 void *buf)
114966c7723aSAndrew Jeffery {
115066c7723aSAndrew Jeffery 	static_assert(std::is_same<uint32_t *, T>::value);
115166c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint32(ctx, buf);
115266c7723aSAndrew Jeffery }
115366c7723aSAndrew Jeffery 
115466c7723aSAndrew Jeffery template <typename T>
pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf * ctx,void * buf)115566c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
115666c7723aSAndrew Jeffery 						void *buf)
115766c7723aSAndrew Jeffery {
115866c7723aSAndrew Jeffery 	static_assert(std::is_same<int32_t *, T>::value);
115966c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int32(ctx, buf);
116066c7723aSAndrew Jeffery }
116166c7723aSAndrew Jeffery 
116266c7723aSAndrew Jeffery template <typename T>
pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf * ctx,void * buf)116366c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
116466c7723aSAndrew Jeffery 						 void *buf)
116566c7723aSAndrew Jeffery {
116666c7723aSAndrew Jeffery 	static_assert(std::is_same<real32_t *, T>::value);
116766c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_real32(ctx, buf);
116866c7723aSAndrew Jeffery }
116966c7723aSAndrew Jeffery #endif
117066c7723aSAndrew Jeffery 
1171c63f63a2SAndrew Jeffery #endif /* BUF_H */
1172