xref: /openbmc/libpldm/src/msgbuf.h (revision a065eccb)
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 <libpldm/base.h>
17 #include <libpldm/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 	if (!ctx || !ctx->cursor || !dst) {
347 		return PLDM_ERROR_INVALID_DATA;
348 	}
349 
350 	if (!count) {
351 		return PLDM_SUCCESS;
352 	}
353 
354 	if (count >= SSIZE_MAX) {
355 		return PLDM_ERROR_INVALID_LENGTH;
356 	}
357 
358 	ctx->remaining -= (ssize_t)count;
359 	assert(ctx->remaining >= 0);
360 	if (ctx->remaining < 0) {
361 		return PLDM_ERROR_INVALID_LENGTH;
362 	}
363 
364 	memcpy(dst, ctx->cursor, count);
365 	ctx->cursor += count;
366 
367 	return PLDM_SUCCESS;
368 }
369 
370 #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
371 	_Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
372 								     count)
373 
374 static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
375 					    const uint32_t src)
376 {
377 	uint32_t val = htole32(src);
378 
379 	if (!ctx || !ctx->cursor) {
380 		return PLDM_ERROR_INVALID_DATA;
381 	}
382 
383 	ctx->remaining -= sizeof(src);
384 	assert(ctx->remaining >= 0);
385 	if (ctx->remaining < 0) {
386 		return PLDM_ERROR_INVALID_LENGTH;
387 	}
388 
389 	memcpy(ctx->cursor, &val, sizeof(val));
390 	ctx->cursor += sizeof(src);
391 
392 	return PLDM_SUCCESS;
393 }
394 
395 static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
396 					    const uint16_t src)
397 {
398 	uint16_t val = htole16(src);
399 
400 	if (!ctx || !ctx->cursor) {
401 		return PLDM_ERROR_INVALID_DATA;
402 	}
403 
404 	ctx->remaining -= sizeof(src);
405 	assert(ctx->remaining >= 0);
406 	if (ctx->remaining < 0) {
407 		return PLDM_ERROR_INVALID_LENGTH;
408 	}
409 
410 	memcpy(ctx->cursor, &val, sizeof(val));
411 	ctx->cursor += sizeof(src);
412 
413 	return PLDM_SUCCESS;
414 }
415 
416 static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
417 					   const uint8_t src)
418 {
419 	if (!ctx || !ctx->cursor) {
420 		return PLDM_ERROR_INVALID_DATA;
421 	}
422 
423 	ctx->remaining -= sizeof(src);
424 	assert(ctx->remaining >= 0);
425 	if (ctx->remaining < 0) {
426 		return PLDM_ERROR_INVALID_LENGTH;
427 	}
428 
429 	memcpy(ctx->cursor, &src, sizeof(src));
430 	ctx->cursor += sizeof(src);
431 
432 	return PLDM_SUCCESS;
433 }
434 
435 static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
436 					   const int32_t src)
437 {
438 	int32_t val = htole32(src);
439 
440 	if (!ctx || !ctx->cursor) {
441 		return PLDM_ERROR_INVALID_DATA;
442 	}
443 
444 	ctx->remaining -= sizeof(src);
445 	assert(ctx->remaining >= 0);
446 	if (ctx->remaining < 0) {
447 		return PLDM_ERROR_INVALID_LENGTH;
448 	}
449 
450 	memcpy(ctx->cursor, &val, sizeof(val));
451 	ctx->cursor += sizeof(src);
452 
453 	return PLDM_SUCCESS;
454 }
455 
456 static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
457 					   const int16_t src)
458 {
459 	int16_t val = htole16(src);
460 
461 	if (!ctx || !ctx->cursor) {
462 		return PLDM_ERROR_INVALID_DATA;
463 	}
464 
465 	ctx->remaining -= sizeof(src);
466 	assert(ctx->remaining >= 0);
467 	if (ctx->remaining < 0) {
468 		return PLDM_ERROR_INVALID_LENGTH;
469 	}
470 
471 	memcpy(ctx->cursor, &val, sizeof(val));
472 	ctx->cursor += sizeof(src);
473 
474 	return PLDM_SUCCESS;
475 }
476 
477 static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
478 					  const int8_t src)
479 {
480 	if (!ctx || !ctx->cursor) {
481 		return PLDM_ERROR_INVALID_DATA;
482 	}
483 
484 	ctx->remaining -= sizeof(src);
485 	assert(ctx->remaining >= 0);
486 	if (ctx->remaining < 0) {
487 		return PLDM_ERROR_INVALID_LENGTH;
488 	}
489 
490 	memcpy(ctx->cursor, &src, sizeof(src));
491 	ctx->cursor += sizeof(src);
492 
493 	return PLDM_SUCCESS;
494 }
495 
496 #define pldm_msgbuf_insert(dst, src)                                           \
497 	_Generic((src),                                                        \
498 		uint8_t: pldm_msgbuf_insert_uint8,                             \
499 		int8_t: pldm_msgbuf_insert_int8,                               \
500 		uint16_t: pldm_msgbuf_insert_uint16,                           \
501 		int16_t: pldm_msgbuf_insert_int16,                             \
502 		uint32_t: pldm_msgbuf_insert_uint32,                           \
503 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
504 
505 static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
506 						 const uint8_t *src,
507 						 size_t count)
508 {
509 	if (!ctx || !ctx->cursor || !src) {
510 		return PLDM_ERROR_INVALID_DATA;
511 	}
512 
513 	if (!count) {
514 		return PLDM_SUCCESS;
515 	}
516 
517 	if (count >= SSIZE_MAX) {
518 		return PLDM_ERROR_INVALID_LENGTH;
519 	}
520 
521 	ctx->remaining -= (ssize_t)count;
522 	assert(ctx->remaining >= 0);
523 	if (ctx->remaining < 0) {
524 		return PLDM_ERROR_INVALID_LENGTH;
525 	}
526 
527 	memcpy(ctx->cursor, src, count);
528 	ctx->cursor += count;
529 
530 	return PLDM_SUCCESS;
531 }
532 
533 #define pldm_msgbuf_insert_array(dst, src, count)                              \
534 	_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src,  \
535 								    count)
536 
537 static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
538 					    size_t required, void **cursor)
539 {
540 	if (!ctx || !ctx->cursor || !cursor || *cursor) {
541 		return PLDM_ERROR_INVALID_DATA;
542 	}
543 
544 	if (required > SSIZE_MAX) {
545 		return PLDM_ERROR_INVALID_LENGTH;
546 	}
547 
548 	ctx->remaining -= (ssize_t)required;
549 	assert(ctx->remaining >= 0);
550 	if (ctx->remaining < 0) {
551 		return PLDM_ERROR_INVALID_LENGTH;
552 	}
553 
554 	*cursor = ctx->cursor;
555 	ctx->cursor += required;
556 
557 	return PLDM_SUCCESS;
558 }
559 
560 static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
561 					     void **cursor, size_t *len)
562 {
563 	if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
564 		return PLDM_ERROR_INVALID_DATA;
565 	}
566 
567 	assert(ctx->remaining >= 0);
568 	if (ctx->remaining < 0) {
569 		return PLDM_ERROR_INVALID_LENGTH;
570 	}
571 
572 	*cursor = ctx->cursor;
573 	ctx->cursor += ctx->remaining;
574 	*len = ctx->remaining;
575 	ctx->remaining = 0;
576 
577 	return PLDM_SUCCESS;
578 }
579 #ifdef __cplusplus
580 }
581 #endif
582 
583 #endif /* BUF_H */
584