xref: /openbmc/libpldm/src/msgbuf.h (revision 369b121a)
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 /**
81c63f63a2SAndrew Jeffery  * @brief Destroy the pldm buf
82c63f63a2SAndrew Jeffery  *
83c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
84c63f63a2SAndrew Jeffery  *
85c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
86c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
87c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
88c63f63a2SAndrew Jeffery  * bounds of the buffer.
89c63f63a2SAndrew Jeffery  */
90c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
91c63f63a2SAndrew Jeffery {
92c63f63a2SAndrew Jeffery 	int valid;
93c63f63a2SAndrew Jeffery 
94c63f63a2SAndrew Jeffery 	if (!ctx) {
95c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
96c63f63a2SAndrew Jeffery 	}
97c63f63a2SAndrew Jeffery 
98c63f63a2SAndrew Jeffery 	valid = pldm_msgbuf_validate(ctx);
99c63f63a2SAndrew Jeffery 
100c63f63a2SAndrew Jeffery 	ctx->cursor = NULL;
101c63f63a2SAndrew Jeffery 	ctx->remaining = 0;
102c63f63a2SAndrew Jeffery 
103c63f63a2SAndrew Jeffery 	return valid;
104c63f63a2SAndrew Jeffery }
105c63f63a2SAndrew Jeffery 
106c63f63a2SAndrew Jeffery /**
107c63f63a2SAndrew Jeffery  * @brief pldm_msgbuf extractor for a uint8_t
108c63f63a2SAndrew Jeffery  *
109c63f63a2SAndrew Jeffery  * @param[inout] ctx - pldm_msgbuf context for extractor
110c63f63a2SAndrew Jeffery  * @param[out] dst - destination of extracted value
111c63f63a2SAndrew Jeffery  *
112c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
113c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH otherwise.
114c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
115c63f63a2SAndrew Jeffery  */
116c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx,
117c63f63a2SAndrew Jeffery 					    uint8_t *dst)
118c63f63a2SAndrew Jeffery {
119c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
120c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
121c63f63a2SAndrew Jeffery 	}
122c63f63a2SAndrew Jeffery 
123c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(*dst);
124c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
125c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
126c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
127c63f63a2SAndrew Jeffery 	}
128c63f63a2SAndrew Jeffery 
129c63f63a2SAndrew Jeffery 	*dst = *((uint8_t *)(ctx->cursor));
130c63f63a2SAndrew Jeffery 	ctx->cursor++;
131c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
132c63f63a2SAndrew Jeffery }
133c63f63a2SAndrew Jeffery 
134c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_t *dst)
135c63f63a2SAndrew Jeffery {
136c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
137c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
138c63f63a2SAndrew Jeffery 	}
139c63f63a2SAndrew Jeffery 
140c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(*dst);
141c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
142c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
143c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
144c63f63a2SAndrew Jeffery 	}
145c63f63a2SAndrew Jeffery 
146c63f63a2SAndrew Jeffery 	*dst = *((int8_t *)(ctx->cursor));
147c63f63a2SAndrew Jeffery 	ctx->cursor++;
148c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
149c63f63a2SAndrew Jeffery }
150c63f63a2SAndrew Jeffery 
151c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
152c63f63a2SAndrew Jeffery 					     uint16_t *dst)
153c63f63a2SAndrew Jeffery {
154c63f63a2SAndrew Jeffery 	uint16_t ldst;
155c63f63a2SAndrew Jeffery 
156c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
157c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
158c63f63a2SAndrew Jeffery 	}
159c63f63a2SAndrew Jeffery 
160c63f63a2SAndrew Jeffery 	// Check for buffer overflow. If we overflow, account for the request as
161c63f63a2SAndrew Jeffery 	// negative values in ctx->remaining. This way we can debug how far
162c63f63a2SAndrew Jeffery 	// we've overflowed.
163c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
164c63f63a2SAndrew Jeffery 
165c63f63a2SAndrew Jeffery 	// Prevent the access if it would overflow. First, assert so we blow up
166c63f63a2SAndrew Jeffery 	// the test suite right at the point of failure. However, cater to
167c63f63a2SAndrew Jeffery 	// -DNDEBUG by explicitly testing that the access is valid.
168c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
169c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
170c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
171c63f63a2SAndrew Jeffery 	}
172c63f63a2SAndrew Jeffery 
173c63f63a2SAndrew Jeffery 	// Use memcpy() to have the compiler deal with any alignment
174c63f63a2SAndrew Jeffery 	// issues on the target architecture
175c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
176c63f63a2SAndrew Jeffery 
177c63f63a2SAndrew Jeffery 	// Only assign the target value once it's correctly decoded
178c63f63a2SAndrew Jeffery 	*dst = le16toh(ldst);
179c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
180c63f63a2SAndrew Jeffery 
181c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
182c63f63a2SAndrew Jeffery }
183c63f63a2SAndrew Jeffery 
184c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx,
185c63f63a2SAndrew Jeffery 					    int16_t *dst)
186c63f63a2SAndrew Jeffery {
187c63f63a2SAndrew Jeffery 	int16_t ldst;
188c63f63a2SAndrew Jeffery 
189c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
190c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
191c63f63a2SAndrew Jeffery 	}
192c63f63a2SAndrew Jeffery 
193c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
194c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
195c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
196c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
197c63f63a2SAndrew Jeffery 	}
198c63f63a2SAndrew Jeffery 
199c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
200c63f63a2SAndrew Jeffery 
201c63f63a2SAndrew Jeffery 	*dst = le16toh(ldst);
202c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
203c63f63a2SAndrew Jeffery 
204c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
205c63f63a2SAndrew Jeffery }
206c63f63a2SAndrew Jeffery 
207c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
208c63f63a2SAndrew Jeffery 					     uint32_t *dst)
209c63f63a2SAndrew Jeffery {
210c63f63a2SAndrew Jeffery 	uint32_t ldst;
211c63f63a2SAndrew Jeffery 
212c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
213c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
214c63f63a2SAndrew Jeffery 	}
215c63f63a2SAndrew Jeffery 
216c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
217c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
218c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
219c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
220c63f63a2SAndrew Jeffery 	}
221c63f63a2SAndrew Jeffery 
222c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
223c63f63a2SAndrew Jeffery 
224c63f63a2SAndrew Jeffery 	*dst = le32toh(ldst);
225c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
226c63f63a2SAndrew Jeffery 
227c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
228c63f63a2SAndrew Jeffery }
229c63f63a2SAndrew Jeffery 
230c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx,
231c63f63a2SAndrew Jeffery 					    int32_t *dst)
232c63f63a2SAndrew Jeffery {
233c63f63a2SAndrew Jeffery 	int32_t ldst;
234c63f63a2SAndrew Jeffery 
235c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
236c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
237c63f63a2SAndrew Jeffery 	}
238c63f63a2SAndrew Jeffery 
239c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
240c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
241c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
242c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
243c63f63a2SAndrew Jeffery 	}
244c63f63a2SAndrew Jeffery 
245c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
246c63f63a2SAndrew Jeffery 
247c63f63a2SAndrew Jeffery 	*dst = le32toh(ldst);
248c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
249c63f63a2SAndrew Jeffery 
250c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
251c63f63a2SAndrew Jeffery }
252c63f63a2SAndrew Jeffery 
253c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx,
254c63f63a2SAndrew Jeffery 					     real32_t *dst)
255c63f63a2SAndrew Jeffery {
256c63f63a2SAndrew Jeffery 	uint32_t ldst;
257c63f63a2SAndrew Jeffery 
258c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
259c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
260c63f63a2SAndrew Jeffery 	}
261c63f63a2SAndrew Jeffery 
262c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
263c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
264c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
265c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
266c63f63a2SAndrew Jeffery 	}
267c63f63a2SAndrew Jeffery 
268c63f63a2SAndrew Jeffery 	_Static_assert(sizeof(*dst) == sizeof(ldst),
269c63f63a2SAndrew Jeffery 		       "Mismatched type sizes for dst and ldst");
270c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
271c63f63a2SAndrew Jeffery 	ldst = le32toh(ldst);
272c63f63a2SAndrew Jeffery 	memcpy(dst, &ldst, sizeof(*dst));
273c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(*dst);
274c63f63a2SAndrew Jeffery 
275c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
276c63f63a2SAndrew Jeffery }
277c63f63a2SAndrew Jeffery 
278c63f63a2SAndrew Jeffery #define pldm_msgbuf_extract(ctx, dst)                                          \
279c63f63a2SAndrew Jeffery 	_Generic((*(dst)), uint8_t                                             \
280c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_uint8, int8_t                           \
281c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_int8, uint16_t                          \
282c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_uint16, int16_t                         \
283c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_int16, uint32_t                         \
284c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_uint32, int32_t                         \
285c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_int32, real32_t                         \
286c63f63a2SAndrew Jeffery 		 : pldm_msgbuf_extract_real32)(ctx, dst)
287c63f63a2SAndrew Jeffery 
288*369b121aSAndrew Jeffery static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
289*369b121aSAndrew Jeffery 						  uint8_t *dst, size_t count)
290*369b121aSAndrew Jeffery {
291*369b121aSAndrew Jeffery 	size_t len;
292*369b121aSAndrew Jeffery 
293*369b121aSAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
294*369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
295*369b121aSAndrew Jeffery 	}
296*369b121aSAndrew Jeffery 
297*369b121aSAndrew Jeffery 	if (!count) {
298*369b121aSAndrew Jeffery 		return PLDM_SUCCESS;
299*369b121aSAndrew Jeffery 	}
300*369b121aSAndrew Jeffery 
301*369b121aSAndrew Jeffery 	len = sizeof(*dst) * count;
302*369b121aSAndrew Jeffery 	if (len > SSIZE_MAX) {
303*369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
304*369b121aSAndrew Jeffery 	}
305*369b121aSAndrew Jeffery 
306*369b121aSAndrew Jeffery 	ctx->remaining -= (ssize_t)len;
307*369b121aSAndrew Jeffery 	assert(ctx->remaining >= 0);
308*369b121aSAndrew Jeffery 	if (ctx->remaining < 0) {
309*369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
310*369b121aSAndrew Jeffery 	}
311*369b121aSAndrew Jeffery 
312*369b121aSAndrew Jeffery 	memcpy(dst, ctx->cursor, len);
313*369b121aSAndrew Jeffery 	ctx->cursor += len;
314*369b121aSAndrew Jeffery 
315*369b121aSAndrew Jeffery 	return PLDM_SUCCESS;
316*369b121aSAndrew Jeffery }
317*369b121aSAndrew Jeffery 
318*369b121aSAndrew Jeffery #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
319*369b121aSAndrew Jeffery 	_Generic((*(dst)), uint8_t                                             \
320*369b121aSAndrew Jeffery 		 : pldm_msgbuf_extract_array_uint8)(ctx, dst, count)
321*369b121aSAndrew Jeffery 
322c63f63a2SAndrew Jeffery #ifdef __cplusplus
323c63f63a2SAndrew Jeffery }
324c63f63a2SAndrew Jeffery #endif
325c63f63a2SAndrew Jeffery 
326c63f63a2SAndrew Jeffery #endif /* BUF_H */
327