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