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