xref: /openbmc/libpldm/src/msgbuf.h (revision 062c8762)
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 /*
19  * Fix up C11's _Static_assert() vs C++'s static_assert().
20  *
21  * Can we please have nice things for once.
22  */
23 #ifdef __cplusplus
24 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
25 #define _Static_assert(...) static_assert(__VA_ARGS__)
26 #endif
27 
28 struct pldm_msgbuf {
29 	uint8_t *cursor;
30 	ssize_t remaining;
31 };
32 
33 /**
34  * @brief Initialize pldm buf struct for buf extractor
35  *
36  * @param[out] ctx - pldm_msgbuf context for extractor
37  * @param[in] minsize - The minimum required length of buffer `buf`
38  * @param[in] buf - buffer to be extracted
39  * @param[in] len - size of buffer
40  *
41  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
42  * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
43  * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
44  */
45 __attribute__((no_sanitize("pointer-overflow"))) static inline int
46 pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
47 		 size_t len)
48 {
49 	uint8_t *end;
50 
51 	if (!ctx || !buf) {
52 		return PLDM_ERROR_INVALID_DATA;
53 	}
54 
55 	if ((minsize > len) || (len > SSIZE_MAX)) {
56 		return PLDM_ERROR_INVALID_LENGTH;
57 	}
58 
59 	end = (uint8_t *)buf + len;
60 	if (end && end < (uint8_t *)buf) {
61 		return PLDM_ERROR_INVALID_LENGTH;
62 	}
63 
64 	ctx->cursor = (uint8_t *)buf;
65 	ctx->remaining = (ssize_t)len;
66 
67 	return PLDM_SUCCESS;
68 }
69 
70 /**
71  * @brief Validate buffer overflow state
72  *
73  * @param[in] ctx - pldm_msgbuf context for extractor
74  *
75  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
76  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
77  * prior accesses would have occurred beyond the bounds of the buffer, and
78  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
79  * pointer.
80  */
81 static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
82 {
83 	if (!ctx) {
84 		return PLDM_ERROR_INVALID_DATA;
85 	}
86 
87 	return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
88 }
89 
90 /**
91  * @brief Test whether a message buffer has been exactly consumed
92  *
93  * @param[in] ctx - pldm_msgbuf context for extractor
94  *
95  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
96  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
97  * indicates that an incorrect sequence of accesses have occurred, and
98  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
99  * pointer.
100  */
101 static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
102 {
103 	if (!ctx) {
104 		return PLDM_ERROR_INVALID_DATA;
105 	}
106 
107 	return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
108 }
109 
110 /**
111  * @brief Destroy the pldm buf
112  *
113  * @param[in] ctx - pldm_msgbuf context for extractor
114  *
115  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
116  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
117  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
118  * bounds of the buffer.
119  */
120 static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
121 {
122 	int valid;
123 
124 	if (!ctx) {
125 		return PLDM_ERROR_INVALID_DATA;
126 	}
127 
128 	valid = pldm_msgbuf_validate(ctx);
129 
130 	ctx->cursor = NULL;
131 	ctx->remaining = 0;
132 
133 	return valid;
134 }
135 
136 /**
137  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
138  * has been completely consumed without overflow
139  *
140  * @param[in] ctx - pldm_msgbuf context
141  *
142  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
143  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
144  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
145  * have occurred byond the bounds of the buffer
146  */
147 static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
148 {
149 	int consumed;
150 
151 	if (!ctx) {
152 		return PLDM_ERROR_INVALID_DATA;
153 	}
154 
155 	consumed = pldm_msgbuf_consumed(ctx);
156 
157 	ctx->cursor = NULL;
158 	ctx->remaining = 0;
159 
160 	return consumed;
161 }
162 
163 /**
164  * @brief pldm_msgbuf extractor for a uint8_t
165  *
166  * @param[inout] ctx - pldm_msgbuf context for extractor
167  * @param[out] dst - destination of extracted value
168  *
169  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
170  * PLDM_ERROR_INVALID_LENGTH otherwise.
171  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
172  */
173 static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx,
174 					    uint8_t *dst)
175 {
176 	if (!ctx || !ctx->cursor || !dst) {
177 		return PLDM_ERROR_INVALID_DATA;
178 	}
179 
180 	ctx->remaining -= sizeof(*dst);
181 	assert(ctx->remaining >= 0);
182 	if (ctx->remaining < 0) {
183 		return PLDM_ERROR_INVALID_LENGTH;
184 	}
185 
186 	*dst = *((uint8_t *)(ctx->cursor));
187 	ctx->cursor++;
188 	return PLDM_SUCCESS;
189 }
190 
191 static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_t *dst)
192 {
193 	if (!ctx || !ctx->cursor || !dst) {
194 		return PLDM_ERROR_INVALID_DATA;
195 	}
196 
197 	ctx->remaining -= sizeof(*dst);
198 	assert(ctx->remaining >= 0);
199 	if (ctx->remaining < 0) {
200 		return PLDM_ERROR_INVALID_LENGTH;
201 	}
202 
203 	*dst = *((int8_t *)(ctx->cursor));
204 	ctx->cursor++;
205 	return PLDM_SUCCESS;
206 }
207 
208 static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
209 					     uint16_t *dst)
210 {
211 	uint16_t ldst;
212 
213 	if (!ctx || !ctx->cursor || !dst) {
214 		return PLDM_ERROR_INVALID_DATA;
215 	}
216 
217 	// Check for buffer overflow. If we overflow, account for the request as
218 	// negative values in ctx->remaining. This way we can debug how far
219 	// we've overflowed.
220 	ctx->remaining -= sizeof(ldst);
221 
222 	// Prevent the access if it would overflow. First, assert so we blow up
223 	// the test suite right at the point of failure. However, cater to
224 	// -DNDEBUG by explicitly testing that the access is valid.
225 	assert(ctx->remaining >= 0);
226 	if (ctx->remaining < 0) {
227 		return PLDM_ERROR_INVALID_LENGTH;
228 	}
229 
230 	// Use memcpy() to have the compiler deal with any alignment
231 	// issues on the target architecture
232 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
233 
234 	// Only assign the target value once it's correctly decoded
235 	*dst = le16toh(ldst);
236 	ctx->cursor += sizeof(ldst);
237 
238 	return PLDM_SUCCESS;
239 }
240 
241 static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx,
242 					    int16_t *dst)
243 {
244 	int16_t ldst;
245 
246 	if (!ctx || !ctx->cursor || !dst) {
247 		return PLDM_ERROR_INVALID_DATA;
248 	}
249 
250 	ctx->remaining -= sizeof(ldst);
251 	assert(ctx->remaining >= 0);
252 	if (ctx->remaining < 0) {
253 		return PLDM_ERROR_INVALID_LENGTH;
254 	}
255 
256 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
257 
258 	*dst = le16toh(ldst);
259 	ctx->cursor += sizeof(ldst);
260 
261 	return PLDM_SUCCESS;
262 }
263 
264 static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
265 					     uint32_t *dst)
266 {
267 	uint32_t ldst;
268 
269 	if (!ctx || !ctx->cursor || !dst) {
270 		return PLDM_ERROR_INVALID_DATA;
271 	}
272 
273 	ctx->remaining -= sizeof(ldst);
274 	assert(ctx->remaining >= 0);
275 	if (ctx->remaining < 0) {
276 		return PLDM_ERROR_INVALID_LENGTH;
277 	}
278 
279 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
280 
281 	*dst = le32toh(ldst);
282 	ctx->cursor += sizeof(ldst);
283 
284 	return PLDM_SUCCESS;
285 }
286 
287 static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx,
288 					    int32_t *dst)
289 {
290 	int32_t ldst;
291 
292 	if (!ctx || !ctx->cursor || !dst) {
293 		return PLDM_ERROR_INVALID_DATA;
294 	}
295 
296 	ctx->remaining -= sizeof(ldst);
297 	assert(ctx->remaining >= 0);
298 	if (ctx->remaining < 0) {
299 		return PLDM_ERROR_INVALID_LENGTH;
300 	}
301 
302 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
303 
304 	*dst = le32toh(ldst);
305 	ctx->cursor += sizeof(ldst);
306 
307 	return PLDM_SUCCESS;
308 }
309 
310 static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx,
311 					     real32_t *dst)
312 {
313 	uint32_t ldst;
314 
315 	if (!ctx || !ctx->cursor || !dst) {
316 		return PLDM_ERROR_INVALID_DATA;
317 	}
318 
319 	ctx->remaining -= sizeof(ldst);
320 	assert(ctx->remaining >= 0);
321 	if (ctx->remaining < 0) {
322 		return PLDM_ERROR_INVALID_LENGTH;
323 	}
324 
325 	_Static_assert(sizeof(*dst) == sizeof(ldst),
326 		       "Mismatched type sizes for dst and ldst");
327 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
328 	ldst = le32toh(ldst);
329 	memcpy(dst, &ldst, sizeof(*dst));
330 	ctx->cursor += sizeof(*dst);
331 
332 	return PLDM_SUCCESS;
333 }
334 
335 #define pldm_msgbuf_extract(ctx, dst)                                          \
336 	_Generic((*(dst)), uint8_t                                             \
337 		 : pldm_msgbuf_extract_uint8, int8_t                           \
338 		 : pldm_msgbuf_extract_int8, uint16_t                          \
339 		 : pldm_msgbuf_extract_uint16, int16_t                         \
340 		 : pldm_msgbuf_extract_int16, uint32_t                         \
341 		 : pldm_msgbuf_extract_uint32, int32_t                         \
342 		 : pldm_msgbuf_extract_int32, real32_t                         \
343 		 : pldm_msgbuf_extract_real32)(ctx, dst)
344 
345 static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
346 						  uint8_t *dst, size_t count)
347 {
348 	size_t len;
349 
350 	if (!ctx || !ctx->cursor || !dst) {
351 		return PLDM_ERROR_INVALID_DATA;
352 	}
353 
354 	if (!count) {
355 		return PLDM_SUCCESS;
356 	}
357 
358 	len = sizeof(*dst) * count;
359 	if (len > SSIZE_MAX) {
360 		return PLDM_ERROR_INVALID_LENGTH;
361 	}
362 
363 	ctx->remaining -= (ssize_t)len;
364 	assert(ctx->remaining >= 0);
365 	if (ctx->remaining < 0) {
366 		return PLDM_ERROR_INVALID_LENGTH;
367 	}
368 
369 	memcpy(dst, ctx->cursor, len);
370 	ctx->cursor += len;
371 
372 	return PLDM_SUCCESS;
373 }
374 
375 #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
376 	_Generic((*(dst)), uint8_t                                             \
377 		 : pldm_msgbuf_extract_array_uint8)(ctx, dst, count)
378 
379 static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
380 					    const uint32_t src)
381 {
382 	uint32_t val = htole32(src);
383 
384 	if (!ctx || !ctx->cursor) {
385 		return PLDM_ERROR_INVALID_DATA;
386 	}
387 
388 	ctx->remaining -= sizeof(src);
389 	assert(ctx->remaining >= 0);
390 	if (ctx->remaining < 0) {
391 		return PLDM_ERROR_INVALID_LENGTH;
392 	}
393 
394 	memcpy(ctx->cursor, &val, sizeof(val));
395 	ctx->cursor += sizeof(src);
396 
397 	return PLDM_SUCCESS;
398 }
399 
400 static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
401 					    const uint16_t src)
402 {
403 	uint16_t val = htole16(src);
404 
405 	if (!ctx || !ctx->cursor) {
406 		return PLDM_ERROR_INVALID_DATA;
407 	}
408 
409 	ctx->remaining -= sizeof(src);
410 	assert(ctx->remaining >= 0);
411 	if (ctx->remaining < 0) {
412 		return PLDM_ERROR_INVALID_LENGTH;
413 	}
414 
415 	memcpy(ctx->cursor, &val, sizeof(val));
416 	ctx->cursor += sizeof(src);
417 
418 	return PLDM_SUCCESS;
419 }
420 
421 static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
422 					   const uint8_t src)
423 {
424 	if (!ctx || !ctx->cursor) {
425 		return PLDM_ERROR_INVALID_DATA;
426 	}
427 
428 	ctx->remaining -= sizeof(src);
429 	assert(ctx->remaining >= 0);
430 	if (ctx->remaining < 0) {
431 		return PLDM_ERROR_INVALID_LENGTH;
432 	}
433 
434 	memcpy(ctx->cursor, &src, sizeof(src));
435 	ctx->cursor += sizeof(src);
436 
437 	return PLDM_SUCCESS;
438 }
439 
440 static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
441 					   const int32_t src)
442 {
443 	int32_t val = htole32(src);
444 
445 	if (!ctx || !ctx->cursor) {
446 		return PLDM_ERROR_INVALID_DATA;
447 	}
448 
449 	ctx->remaining -= sizeof(src);
450 	assert(ctx->remaining >= 0);
451 	if (ctx->remaining < 0) {
452 		return PLDM_ERROR_INVALID_LENGTH;
453 	}
454 
455 	memcpy(ctx->cursor, &val, sizeof(val));
456 	ctx->cursor += sizeof(src);
457 
458 	return PLDM_SUCCESS;
459 }
460 
461 static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
462 					   const int16_t src)
463 {
464 	int16_t val = htole16(src);
465 
466 	if (!ctx || !ctx->cursor) {
467 		return PLDM_ERROR_INVALID_DATA;
468 	}
469 
470 	ctx->remaining -= sizeof(src);
471 	assert(ctx->remaining >= 0);
472 	if (ctx->remaining < 0) {
473 		return PLDM_ERROR_INVALID_LENGTH;
474 	}
475 
476 	memcpy(ctx->cursor, &val, sizeof(val));
477 	ctx->cursor += sizeof(src);
478 
479 	return PLDM_SUCCESS;
480 }
481 
482 static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
483 					  const int8_t src)
484 {
485 	if (!ctx || !ctx->cursor) {
486 		return PLDM_ERROR_INVALID_DATA;
487 	}
488 
489 	ctx->remaining -= sizeof(src);
490 	assert(ctx->remaining >= 0);
491 	if (ctx->remaining < 0) {
492 		return PLDM_ERROR_INVALID_LENGTH;
493 	}
494 
495 	memcpy(ctx->cursor, &src, sizeof(src));
496 	ctx->cursor += sizeof(src);
497 
498 	return PLDM_SUCCESS;
499 }
500 
501 #define pldm_msgbuf_insert(dst, src)                                           \
502 	_Generic((src), uint8_t                                                \
503 		 : pldm_msgbuf_insert_uint8, int8_t                            \
504 		 : pldm_msgbuf_insert_int8, uint16_t                           \
505 		 : pldm_msgbuf_insert_uint16, int16_t                          \
506 		 : pldm_msgbuf_insert_int16, uint32_t                          \
507 		 : pldm_msgbuf_insert_uint32, int32_t                          \
508 		 : pldm_msgbuf_insert_int32)(dst, src)
509 
510 static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
511 						 const uint8_t *src,
512 						 size_t count)
513 {
514 	size_t len;
515 	if (!ctx || !ctx->cursor || !src) {
516 		return PLDM_ERROR_INVALID_DATA;
517 	}
518 
519 	if (!count) {
520 		return PLDM_SUCCESS;
521 	}
522 
523 	len = sizeof(*src) * count;
524 	if (len > SSIZE_MAX) {
525 		return PLDM_ERROR_INVALID_LENGTH;
526 	}
527 
528 	ctx->remaining -= (ssize_t)len;
529 	assert(ctx->remaining >= 0);
530 	if (ctx->remaining < 0) {
531 		return PLDM_ERROR_INVALID_LENGTH;
532 	}
533 
534 	memcpy(ctx->cursor, src, len);
535 	ctx->cursor += len;
536 
537 	return PLDM_SUCCESS;
538 }
539 
540 #define pldm_msgbuf_insert_array(dst, src, count)                              \
541 	_Generic((*(src)), uint8_t                                             \
542 		 : pldm_msgbuf_insert_array_uint8)(dst, src, count)
543 
544 static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
545 					    size_t required, void **cursor)
546 {
547 	if (!ctx || !ctx->cursor || !cursor || *cursor) {
548 		return PLDM_ERROR_INVALID_DATA;
549 	}
550 
551 	if (required > SSIZE_MAX) {
552 		return PLDM_ERROR_INVALID_LENGTH;
553 	}
554 
555 	ctx->remaining -= (ssize_t)required;
556 	assert(ctx->remaining >= 0);
557 	if (ctx->remaining < 0) {
558 		return PLDM_ERROR_INVALID_LENGTH;
559 	}
560 
561 	*cursor = ctx->cursor;
562 	ctx->cursor += required;
563 
564 	return PLDM_SUCCESS;
565 }
566 
567 static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
568 					     void **cursor, size_t *len)
569 {
570 	if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
571 		return PLDM_ERROR_INVALID_DATA;
572 	}
573 
574 	assert(ctx->remaining >= 0);
575 	if (ctx->remaining < 0) {
576 		return PLDM_ERROR_INVALID_LENGTH;
577 	}
578 
579 	*cursor = ctx->cursor;
580 	ctx->cursor += ctx->remaining;
581 	*len = ctx->remaining;
582 	ctx->remaining = 0;
583 
584 	return PLDM_SUCCESS;
585 }
586 #ifdef __cplusplus
587 }
588 #endif
589 
590 #endif /* BUF_H */
591