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