xref: /openbmc/libpldm/src/msgbuf.h (revision db7b8324)
1c63f63a2SAndrew Jeffery #ifndef PLDM_MSGBUF_H
2c63f63a2SAndrew Jeffery #define PLDM_MSGBUF_H
3c63f63a2SAndrew Jeffery 
4c63f63a2SAndrew Jeffery #ifdef __cplusplus
5c63f63a2SAndrew Jeffery extern "C" {
6c63f63a2SAndrew Jeffery #endif
7c63f63a2SAndrew Jeffery 
8c63f63a2SAndrew Jeffery #include "base.h"
9c63f63a2SAndrew Jeffery #include "pldm_types.h"
10c63f63a2SAndrew Jeffery 
11c63f63a2SAndrew Jeffery #include <assert.h>
12c63f63a2SAndrew Jeffery #include <endian.h>
13c63f63a2SAndrew Jeffery #include <limits.h>
14c63f63a2SAndrew Jeffery #include <stdbool.h>
15c63f63a2SAndrew Jeffery #include <string.h>
16c63f63a2SAndrew Jeffery #include <sys/types.h>
17c63f63a2SAndrew Jeffery 
18c63f63a2SAndrew Jeffery struct pldm_msgbuf {
19c63f63a2SAndrew Jeffery 	const uint8_t *cursor;
20c63f63a2SAndrew Jeffery 	ssize_t remaining;
21c63f63a2SAndrew Jeffery };
22c63f63a2SAndrew Jeffery 
23c63f63a2SAndrew Jeffery /**
24c63f63a2SAndrew Jeffery  * @brief Initialize pldm buf struct for buf extractor
25c63f63a2SAndrew Jeffery  *
26c63f63a2SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
27c63f63a2SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
28c63f63a2SAndrew Jeffery  * @param[in] buf - buffer to be extracted
29c63f63a2SAndrew Jeffery  * @param[in] len - size of buffer
30c63f63a2SAndrew Jeffery  *
31c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
32c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
33c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
34c63f63a2SAndrew Jeffery  */
355646f23bSAndrew Jeffery __attribute__((no_sanitize("pointer-overflow"))) static inline int
365646f23bSAndrew Jeffery pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
375646f23bSAndrew Jeffery 		 size_t len)
38c63f63a2SAndrew Jeffery {
39c63f63a2SAndrew Jeffery 	uint8_t *end;
40c63f63a2SAndrew Jeffery 
41c63f63a2SAndrew Jeffery 	if (!ctx || !buf) {
42c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
43c63f63a2SAndrew Jeffery 	}
44c63f63a2SAndrew Jeffery 
45c63f63a2SAndrew Jeffery 	if ((minsize > len) || (len > SSIZE_MAX)) {
46c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
47c63f63a2SAndrew Jeffery 	}
48c63f63a2SAndrew Jeffery 
49c63f63a2SAndrew Jeffery 	end = (uint8_t *)buf + len;
50c63f63a2SAndrew Jeffery 	if (end && end < (uint8_t *)buf) {
51c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
52c63f63a2SAndrew Jeffery 	}
53c63f63a2SAndrew Jeffery 
54c63f63a2SAndrew Jeffery 	ctx->cursor = (uint8_t *)buf;
55c63f63a2SAndrew Jeffery 	ctx->remaining = (ssize_t)len;
56c63f63a2SAndrew Jeffery 
57c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
58c63f63a2SAndrew Jeffery }
59c63f63a2SAndrew Jeffery 
60c63f63a2SAndrew Jeffery /**
61c63f63a2SAndrew Jeffery  * @brief Validate buffer overflow state
62c63f63a2SAndrew Jeffery  *
63c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
64c63f63a2SAndrew Jeffery  *
65c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
66c63f63a2SAndrew Jeffery  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
67c63f63a2SAndrew Jeffery  * prior accesses would have occurred beyond the bounds of the buffer, and
68c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
69c63f63a2SAndrew Jeffery  * pointer.
70c63f63a2SAndrew Jeffery  */
71c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
72c63f63a2SAndrew Jeffery {
73c63f63a2SAndrew Jeffery 	if (!ctx) {
74c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
75c63f63a2SAndrew Jeffery 	}
76c63f63a2SAndrew Jeffery 
77c63f63a2SAndrew Jeffery 	return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
78c63f63a2SAndrew Jeffery }
79c63f63a2SAndrew Jeffery 
80c63f63a2SAndrew Jeffery /**
81*db7b8324SAndrew Jeffery  * @brief Test whether a message buffer has been exactly consumed
82*db7b8324SAndrew Jeffery  *
83*db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
84*db7b8324SAndrew Jeffery  *
85*db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
86*db7b8324SAndrew Jeffery  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
87*db7b8324SAndrew Jeffery  * indicates that an incorrect sequence of accesses have occurred, and
88*db7b8324SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
89*db7b8324SAndrew Jeffery  * pointer.
90*db7b8324SAndrew Jeffery  */
91*db7b8324SAndrew Jeffery static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
92*db7b8324SAndrew Jeffery {
93*db7b8324SAndrew Jeffery 	if (!ctx) {
94*db7b8324SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
95*db7b8324SAndrew Jeffery 	}
96*db7b8324SAndrew Jeffery 
97*db7b8324SAndrew Jeffery 	return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
98*db7b8324SAndrew Jeffery }
99*db7b8324SAndrew Jeffery 
100*db7b8324SAndrew Jeffery /**
101c63f63a2SAndrew Jeffery  * @brief Destroy the pldm buf
102c63f63a2SAndrew Jeffery  *
103c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
104c63f63a2SAndrew Jeffery  *
105c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
106c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
107c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
108c63f63a2SAndrew Jeffery  * bounds of the buffer.
109c63f63a2SAndrew Jeffery  */
110c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
111c63f63a2SAndrew Jeffery {
112c63f63a2SAndrew Jeffery 	int valid;
113c63f63a2SAndrew Jeffery 
114c63f63a2SAndrew Jeffery 	if (!ctx) {
115c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
116c63f63a2SAndrew Jeffery 	}
117c63f63a2SAndrew Jeffery 
118c63f63a2SAndrew Jeffery 	valid = pldm_msgbuf_validate(ctx);
119c63f63a2SAndrew Jeffery 
120c63f63a2SAndrew Jeffery 	ctx->cursor = NULL;
121c63f63a2SAndrew Jeffery 	ctx->remaining = 0;
122c63f63a2SAndrew Jeffery 
123c63f63a2SAndrew Jeffery 	return valid;
124c63f63a2SAndrew Jeffery }
125c63f63a2SAndrew Jeffery 
126c63f63a2SAndrew Jeffery /**
127*db7b8324SAndrew Jeffery  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
128*db7b8324SAndrew Jeffery  * has been completely consumed without overflow
129*db7b8324SAndrew Jeffery  *
130*db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context
131*db7b8324SAndrew Jeffery  *
132*db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
133*db7b8324SAndrew Jeffery  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
134*db7b8324SAndrew Jeffery  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
135*db7b8324SAndrew Jeffery  * have occurred byond the bounds of the buffer
136*db7b8324SAndrew Jeffery  */
137*db7b8324SAndrew Jeffery static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
138*db7b8324SAndrew Jeffery {
139*db7b8324SAndrew Jeffery 	int consumed;
140*db7b8324SAndrew Jeffery 
141*db7b8324SAndrew Jeffery 	if (!ctx) {
142*db7b8324SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
143*db7b8324SAndrew Jeffery 	}
144*db7b8324SAndrew Jeffery 
145*db7b8324SAndrew Jeffery 	consumed = pldm_msgbuf_consumed(ctx);
146*db7b8324SAndrew Jeffery 
147*db7b8324SAndrew Jeffery 	ctx->cursor = NULL;
148*db7b8324SAndrew Jeffery 	ctx->remaining = 0;
149*db7b8324SAndrew Jeffery 
150*db7b8324SAndrew Jeffery 	return consumed;
151*db7b8324SAndrew Jeffery }
152*db7b8324SAndrew Jeffery 
153*db7b8324SAndrew Jeffery /**
154c63f63a2SAndrew Jeffery  * @brief pldm_msgbuf extractor for a uint8_t
155c63f63a2SAndrew Jeffery  *
156c63f63a2SAndrew Jeffery  * @param[inout] ctx - pldm_msgbuf context for extractor
157c63f63a2SAndrew Jeffery  * @param[out] dst - destination of extracted value
158c63f63a2SAndrew Jeffery  *
159c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
160c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH otherwise.
161c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
162c63f63a2SAndrew Jeffery  */
163c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx,
164c63f63a2SAndrew Jeffery 					    uint8_t *dst)
165c63f63a2SAndrew Jeffery {
166c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
167c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
168c63f63a2SAndrew Jeffery 	}
169c63f63a2SAndrew Jeffery 
170c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(*dst);
171c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
172c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
173c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
174c63f63a2SAndrew Jeffery 	}
175c63f63a2SAndrew Jeffery 
176c63f63a2SAndrew Jeffery 	*dst = *((uint8_t *)(ctx->cursor));
177c63f63a2SAndrew Jeffery 	ctx->cursor++;
178c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
179c63f63a2SAndrew Jeffery }
180c63f63a2SAndrew Jeffery 
181c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_t *dst)
182c63f63a2SAndrew Jeffery {
183c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
184c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
185c63f63a2SAndrew Jeffery 	}
186c63f63a2SAndrew Jeffery 
187c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(*dst);
188c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
189c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
190c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
191c63f63a2SAndrew Jeffery 	}
192c63f63a2SAndrew Jeffery 
193c63f63a2SAndrew Jeffery 	*dst = *((int8_t *)(ctx->cursor));
194c63f63a2SAndrew Jeffery 	ctx->cursor++;
195c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
196c63f63a2SAndrew Jeffery }
197c63f63a2SAndrew Jeffery 
198c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
199c63f63a2SAndrew Jeffery 					     uint16_t *dst)
200c63f63a2SAndrew Jeffery {
201c63f63a2SAndrew Jeffery 	uint16_t ldst;
202c63f63a2SAndrew Jeffery 
203c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
204c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
205c63f63a2SAndrew Jeffery 	}
206c63f63a2SAndrew Jeffery 
207c63f63a2SAndrew Jeffery 	// Check for buffer overflow. If we overflow, account for the request as
208c63f63a2SAndrew Jeffery 	// negative values in ctx->remaining. This way we can debug how far
209c63f63a2SAndrew Jeffery 	// we've overflowed.
210c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
211c63f63a2SAndrew Jeffery 
212c63f63a2SAndrew Jeffery 	// Prevent the access if it would overflow. First, assert so we blow up
213c63f63a2SAndrew Jeffery 	// the test suite right at the point of failure. However, cater to
214c63f63a2SAndrew Jeffery 	// -DNDEBUG by explicitly testing that the access is valid.
215c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
216c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
217c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
218c63f63a2SAndrew Jeffery 	}
219c63f63a2SAndrew Jeffery 
220c63f63a2SAndrew Jeffery 	// Use memcpy() to have the compiler deal with any alignment
221c63f63a2SAndrew Jeffery 	// issues on the target architecture
222c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
223c63f63a2SAndrew Jeffery 
224c63f63a2SAndrew Jeffery 	// Only assign the target value once it's correctly decoded
225c63f63a2SAndrew Jeffery 	*dst = le16toh(ldst);
226c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
227c63f63a2SAndrew Jeffery 
228c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
229c63f63a2SAndrew Jeffery }
230c63f63a2SAndrew Jeffery 
231c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx,
232c63f63a2SAndrew Jeffery 					    int16_t *dst)
233c63f63a2SAndrew Jeffery {
234c63f63a2SAndrew Jeffery 	int16_t ldst;
235c63f63a2SAndrew Jeffery 
236c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
237c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
238c63f63a2SAndrew Jeffery 	}
239c63f63a2SAndrew Jeffery 
240c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
241c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
242c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
243c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
244c63f63a2SAndrew Jeffery 	}
245c63f63a2SAndrew Jeffery 
246c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
247c63f63a2SAndrew Jeffery 
248c63f63a2SAndrew Jeffery 	*dst = le16toh(ldst);
249c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
250c63f63a2SAndrew Jeffery 
251c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
252c63f63a2SAndrew Jeffery }
253c63f63a2SAndrew Jeffery 
254c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
255c63f63a2SAndrew Jeffery 					     uint32_t *dst)
256c63f63a2SAndrew Jeffery {
257c63f63a2SAndrew Jeffery 	uint32_t ldst;
258c63f63a2SAndrew Jeffery 
259c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
260c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
261c63f63a2SAndrew Jeffery 	}
262c63f63a2SAndrew Jeffery 
263c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
264c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
265c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
266c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
267c63f63a2SAndrew Jeffery 	}
268c63f63a2SAndrew Jeffery 
269c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
270c63f63a2SAndrew Jeffery 
271c63f63a2SAndrew Jeffery 	*dst = le32toh(ldst);
272c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
273c63f63a2SAndrew Jeffery 
274c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
275c63f63a2SAndrew Jeffery }
276c63f63a2SAndrew Jeffery 
277c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx,
278c63f63a2SAndrew Jeffery 					    int32_t *dst)
279c63f63a2SAndrew Jeffery {
280c63f63a2SAndrew Jeffery 	int32_t ldst;
281c63f63a2SAndrew Jeffery 
282c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
283c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
284c63f63a2SAndrew Jeffery 	}
285c63f63a2SAndrew Jeffery 
286c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
287c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
288c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
289c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
290c63f63a2SAndrew Jeffery 	}
291c63f63a2SAndrew Jeffery 
292c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
293c63f63a2SAndrew Jeffery 
294c63f63a2SAndrew Jeffery 	*dst = le32toh(ldst);
295c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
296c63f63a2SAndrew Jeffery 
297c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
298c63f63a2SAndrew Jeffery }
299c63f63a2SAndrew Jeffery 
300c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx,
301c63f63a2SAndrew Jeffery 					     real32_t *dst)
302c63f63a2SAndrew Jeffery {
303c63f63a2SAndrew Jeffery 	uint32_t ldst;
304c63f63a2SAndrew Jeffery 
305c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
306c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
307c63f63a2SAndrew Jeffery 	}
308c63f63a2SAndrew Jeffery 
309c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
310c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
311c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
312c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
313c63f63a2SAndrew Jeffery 	}
314c63f63a2SAndrew Jeffery 
315c63f63a2SAndrew Jeffery 	_Static_assert(sizeof(*dst) == sizeof(ldst),
316c63f63a2SAndrew Jeffery 		       "Mismatched type sizes for dst and ldst");
317c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
318c63f63a2SAndrew Jeffery 	ldst = le32toh(ldst);
319c63f63a2SAndrew Jeffery 	memcpy(dst, &ldst, sizeof(*dst));
320c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(*dst);
321c63f63a2SAndrew Jeffery 
322c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
323c63f63a2SAndrew Jeffery }
324c63f63a2SAndrew Jeffery 
325c63f63a2SAndrew Jeffery #define pldm_msgbuf_extract(ctx, dst)                                          \
326c63f63a2SAndrew Jeffery 	_Generic((*(dst)), uint8_t                                             \
327c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_uint8, int8_t                           \
328c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_int8, uint16_t                          \
329c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_uint16, int16_t                         \
330c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_int16, uint32_t                         \
331c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_uint32, int32_t                         \
332c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_int32, real32_t                         \
333c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_real32)(ctx, dst)
334c63f63a2SAndrew Jeffery 
335369b121aSAndrew Jeffery static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
336369b121aSAndrew Jeffery 						  uint8_t *dst, size_t count)
337369b121aSAndrew Jeffery {
338369b121aSAndrew Jeffery 	size_t len;
339369b121aSAndrew Jeffery 
340369b121aSAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
341369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
342369b121aSAndrew Jeffery 	}
343369b121aSAndrew Jeffery 
344369b121aSAndrew Jeffery 	if (!count) {
345369b121aSAndrew Jeffery 		return PLDM_SUCCESS;
346369b121aSAndrew Jeffery 	}
347369b121aSAndrew Jeffery 
348369b121aSAndrew Jeffery 	len = sizeof(*dst) * count;
349369b121aSAndrew Jeffery 	if (len > SSIZE_MAX) {
350369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
351369b121aSAndrew Jeffery 	}
352369b121aSAndrew Jeffery 
353369b121aSAndrew Jeffery 	ctx->remaining -= (ssize_t)len;
354369b121aSAndrew Jeffery 	assert(ctx->remaining >= 0);
355369b121aSAndrew Jeffery 	if (ctx->remaining < 0) {
356369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
357369b121aSAndrew Jeffery 	}
358369b121aSAndrew Jeffery 
359369b121aSAndrew Jeffery 	memcpy(dst, ctx->cursor, len);
360369b121aSAndrew Jeffery 	ctx->cursor += len;
361369b121aSAndrew Jeffery 
362369b121aSAndrew Jeffery 	return PLDM_SUCCESS;
363369b121aSAndrew Jeffery }
364369b121aSAndrew Jeffery 
365369b121aSAndrew Jeffery #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
366369b121aSAndrew Jeffery 	_Generic((*(dst)), uint8_t                                             \
367369b121aSAndrew Jeffery 		 : pldm_msgbuf_extract_array_uint8)(ctx, dst, count)
368369b121aSAndrew Jeffery 
369c63f63a2SAndrew Jeffery #ifdef __cplusplus
370c63f63a2SAndrew Jeffery }
371c63f63a2SAndrew Jeffery #endif
372c63f63a2SAndrew Jeffery 
373c63f63a2SAndrew Jeffery #endif /* BUF_H */
374