xref: /openbmc/libpldm/src/msgbuf.h (revision 7939382f)
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 #include "compiler.h"
6 
7 /*
8  * Historically, many of the structs exposed in libpldm's public headers are
9  * defined with __attribute__((packed)). This is unfortunate: it gives the
10  * impression that a wire-format buffer can be cast to the message type to make
11  * the message's fields easily accessible. As it turns out, that's not
12  * that's valid for several reasons:
13  *
14  * 1. Casting the wire-format buffer to a struct of the message type doesn't
15  *    abstract the endianness of message field values
16  *
17  * 2. Some messages contain packed tagged union fields which cannot be properly
18  *    described in a C struct.
19  *
20  * The msgbuf APIs exist to assist with (un)packing the wire-format in a way
21  * that is type-safe, spatially memory-safe, endian-safe, performant, and
22  * free of undefined-behaviour. Message structs that are added to the public
23  * library API should no-longer be marked __attribute__((packed)), and the
24  * implementation of their encode and decode functions must exploit the msgbuf
25  * API.
26  *
27  * However, we would like to allow implementation of codec functions in terms of
28  * msgbuf APIs even if they're decoding a message into a (historically) packed
29  * struct. Some of the complexity that follows is a consequence of the packed/
30  * unpacked conflict.
31  */
32 
33 #ifdef __cplusplus
34 /*
35  * Fix up C11's _Static_assert() vs C++'s static_assert().
36  *
37  * Can we please have nice things for once.
38  */
39 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
40 #define _Static_assert(...) static_assert(__VA_ARGS__)
41 extern "C" {
42 #endif
43 
44 #include <libpldm/base.h>
45 #include <libpldm/pldm_types.h>
46 
47 #include "compiler.h"
48 
49 #include <assert.h>
50 #include <endian.h>
51 #include <errno.h>
52 #include <limits.h>
53 #include <stdbool.h>
54 #include <stdint.h>
55 #include <string.h>
56 #include <sys/types.h>
57 #include <uchar.h>
58 
59 /*
60  * We can't use static_assert() outside of some other C construct. Deal
61  * with high-level global assertions by burying them in an unused struct
62  * declaration, that has a sole member for compliance with the requirement that
63  * types must have a size.
64 */
65 static struct {
66 	static_assert(
67 		INTMAX_MAX != SIZE_MAX,
68 		"Extraction and insertion value comparisons may be broken");
69 	static_assert(INTMAX_MIN + INTMAX_MAX <= 0,
70 		      "Extraction and insertion arithmetic may be broken");
71 	static_assert(PLDM_SUCCESS == 0, "Error handling is broken");
72 	int compliance;
73 } build_assertions LIBPLDM_CC_UNUSED;
74 
75 enum pldm_msgbuf_error_mode {
76 	PLDM_MSGBUF_PLDM_CC = 0x5a,
77 	PLDM_MSGBUF_C_ERRNO = 0xa5,
78 };
79 
80 struct pldm_msgbuf {
81 	uint8_t *cursor;
82 	intmax_t remaining;
83 	enum pldm_msgbuf_error_mode mode;
84 };
85 
86 /**
87  * @brief Either negate an errno value or return a value mapped to a PLDM
88  * completion code.
89  *
90  * Note that `pldm_msgbuf_status()` is purely internal to the msgbuf API
91  * for ergonomics. It's preferred that we don't try to unify this with
92  * `pldm_xlate_errno()` from src/api.h despite the similarities.
93  *
94  * @param[in] ctx - The msgbuf context providing the personality info
95  * @param[in] err - The positive errno value to translate
96  *
97  * @return Either the negated value of @p err if the context's error mode is
98  *         `PLDM_MSGBUF_C_ERRNO`, or the equivalent PLDM completion code if the
99  *         error mode is `PLDM_MSGBUF_PLDM_CC`.
100  */
101 LIBPLDM_CC_NONNULL
pldm_msgbuf_status(struct pldm_msgbuf * ctx,unsigned int err)102 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_status(struct pldm_msgbuf *ctx,
103 						unsigned int err)
104 {
105 	int rc;
106 
107 	assert(err != 0);
108 	assert(err <= INT_MAX);
109 
110 	if (ctx->mode == PLDM_MSGBUF_C_ERRNO) {
111 		if (err > INT_MAX) {
112 			return -EINVAL;
113 		}
114 
115 		static_assert(INT_MIN + INT_MAX < 0,
116 			      "Arithmetic assumption failure");
117 		return -((int)err);
118 	}
119 
120 	if (err > INT_MAX) {
121 		return PLDM_ERROR;
122 	}
123 
124 	assert(ctx->mode == PLDM_MSGBUF_PLDM_CC);
125 	switch (err) {
126 	case EINVAL:
127 		rc = PLDM_ERROR_INVALID_DATA;
128 		break;
129 	case EBADMSG:
130 	case EOVERFLOW:
131 		rc = PLDM_ERROR_INVALID_LENGTH;
132 		break;
133 	default:
134 		assert(false);
135 		rc = PLDM_ERROR;
136 		break;
137 	}
138 
139 	assert(rc > 0);
140 	return rc;
141 }
142 
143 /**
144  * @brief Initialize pldm buf struct for buf extractor
145  *
146  * @param[out] ctx - pldm_msgbuf context for extractor
147  * @param[in] minsize - The minimum required length of buffer `buf`
148  * @param[in] buf - buffer to be extracted
149  * @param[in] len - size of buffer
150  *
151  * @return 0 on success, otherwise an error code appropriate for the current
152  *         personality.
153  */
154 LIBPLDM_CC_NONNULL
155 LIBPLDM_CC_ALWAYS_INLINE int
156 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_init(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)157 pldm__msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
158 		  size_t len)
159 {
160 	assert(ctx->mode == PLDM_MSGBUF_PLDM_CC ||
161 	       ctx->mode == PLDM_MSGBUF_C_ERRNO);
162 
163 	if ((minsize > len)) {
164 		return pldm_msgbuf_status(ctx, EOVERFLOW);
165 	}
166 
167 #if INTMAX_MAX < SIZE_MAX
168 	if (len > INTMAX_MAX) {
169 		return pldm_msgbuf_status(ctx, EOVERFLOW);
170 	}
171 #endif
172 
173 	if ((uintptr_t)buf + len < len) {
174 		return pldm_msgbuf_status(ctx, EOVERFLOW);
175 	}
176 
177 	ctx->cursor = (uint8_t *)buf;
178 	ctx->remaining = (intmax_t)len;
179 
180 	return 0;
181 }
182 
183 /**
184  * @brief Initialise a msgbuf instance to return errors as PLDM completion codes
185  *
186  * @see pldm__msgbuf_init
187  *
188  * @param[out] ctx - pldm_msgbuf context for extractor
189  * @param[in] minsize - The minimum required length of buffer `buf`
190  * @param[in] buf - buffer to be extracted
191  * @param[in] len - size of buffer
192  *
193  * @return PLDM_SUCCESS if the provided buffer region is sensible,
194  *         otherwise PLDM_ERROR_INVALID_DATA if pointer parameters are invalid,
195  *         or PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
196  */
197 LIBPLDM_CC_NONNULL
pldm_msgbuf_init_cc(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)198 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_init_cc(struct pldm_msgbuf *ctx,
199 						 size_t minsize,
200 						 const void *buf, size_t len)
201 {
202 	ctx->mode = PLDM_MSGBUF_PLDM_CC;
203 	return pldm__msgbuf_init(ctx, minsize, buf, len);
204 }
205 
206 /**
207  * @brief Initialise a msgbuf instance to return errors as negative errno values
208  *
209  * @see pldm__msgbuf_init
210  *
211  * @param[out] ctx - pldm_msgbuf context for extractor
212  * @param[in] minsize - The minimum required length of buffer `buf`
213  * @param[in] buf - buffer to be extracted
214  * @param[in] len - size of buffer
215  *
216  * @return 0 if the provided buffer region is sensible, otherwise -EINVAL if
217  *         pointer parameters are invalid, or -EOVERFLOW if length constraints
218  *         are violated.
219  */
220 LIBPLDM_CC_NONNULL
pldm_msgbuf_init_errno(struct pldm_msgbuf * ctx,size_t minsize,const void * buf,size_t len)221 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx,
222 						    size_t minsize,
223 						    const void *buf, size_t len)
224 {
225 	ctx->mode = PLDM_MSGBUF_C_ERRNO;
226 	return pldm__msgbuf_init(ctx, minsize, buf, len);
227 }
228 
229 /**
230  * @brief Validate buffer overflow state
231  *
232  * @param[in] ctx - pldm_msgbuf context for extractor
233  *
234  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
235  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
236  * prior accesses would have occurred beyond the bounds of the buffer, and
237  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
238  * pointer.
239  */
240 LIBPLDM_CC_NONNULL
pldm_msgbuf_validate(struct pldm_msgbuf * ctx)241 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
242 {
243 	if (ctx->remaining < 0) {
244 		return pldm_msgbuf_status(ctx, EOVERFLOW);
245 	}
246 
247 	return 0;
248 }
249 
250 /**
251  * @brief Test whether a message buffer has been exactly consumed
252  *
253  * @param[in] ctx - pldm_msgbuf context for extractor
254  *
255  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
256  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
257  * indicates that an incorrect sequence of accesses have occurred, and
258  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
259  * pointer.
260  */
261 LIBPLDM_CC_NONNULL
pldm_msgbuf_consumed(struct pldm_msgbuf * ctx)262 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
263 {
264 	if (ctx->remaining != 0) {
265 		return pldm_msgbuf_status(ctx, EBADMSG);
266 	}
267 
268 	return 0;
269 }
270 
271 /**
272  * @brief Destroy the pldm buf
273  *
274  * @param[in] ctx - pldm_msgbuf context for extractor
275  *
276  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
277  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
278  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
279  * bounds of the buffer.
280  */
281 LIBPLDM_CC_NONNULL
pldm_msgbuf_destroy(struct pldm_msgbuf * ctx)282 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
283 {
284 	int valid;
285 
286 	valid = pldm_msgbuf_validate(ctx);
287 
288 	ctx->cursor = NULL;
289 	ctx->remaining = 0;
290 
291 	return valid;
292 }
293 
294 /**
295  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
296  * has been completely consumed without overflow
297  *
298  * @param[in] ctx - pldm_msgbuf context
299  *
300  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
301  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
302  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
303  * have occurred byond the bounds of the buffer
304  */
305 LIBPLDM_CC_NONNULL
306 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_destroy_consumed(struct pldm_msgbuf * ctx)307 pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
308 {
309 	int consumed;
310 
311 	consumed = pldm_msgbuf_consumed(ctx);
312 
313 	ctx->cursor = NULL;
314 	ctx->remaining = 0;
315 
316 	return consumed;
317 }
318 
319 /*
320  * Exploit the pre-processor to perform type checking by macro substitution.
321  *
322  * A C type is defined by its alignment as well as its object
323  * size, and compilers have a hammer to enforce it in the form of
324  * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
325  * the libpldm public API this presents a problem: Naively attempting to use the
326  * msgbuf APIs on a member of a packed struct would yield an error.
327  *
328  * The msgbuf APIs are implemented such that data is moved through unaligned
329  * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
330  * make the object pointers take a trip through `void *` at its API boundary.
331  * That presents a bit too much of an opportunity to non-surgically remove your
332  * own foot, so here we set about doing something to mitigate that as well.
333  *
334  * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
335  * only for the purpose of object sizes, disregarding alignment. We have a few
336  * constraints that cause some headaches:
337  *
338  * 1. We have to perform the type-check before a call through a C function,
339  *    as the function must take the object pointer argument as `void *`.
340  *    Essentially, this constrains us to doing something with macros.
341  *
342  * 2. While libpldm is a C library, its test suite is written in C++ to take
343  *    advantage of gtest.
344  *
345  * 3. Ideally we'd do something with C's `static_assert()`, however
346  *    `static_assert()` is defined as void, and as we're constrained to macros,
347  *    using `static_assert()` would require a statement-expression
348  *
349  * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
350  *    are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
351  *    the purpose of enabling statement-expressions in this one instance.
352  *
353  * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
354  *    however it's implemented in terms of `_Generic()`, which is not available
355  *    in C++.
356  *
357  * Combined this means we need separate solutions for C and C++.
358  *
359  * For C, as we don't have statement-expressions, we need to exploit some other
360  * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
361  * API function call. We also have to take care of the fact that the call-sites
362  * may be in the context of a variable assignment for error-handling purposes.
363  * The key observation is that we can use the comma operator as a sequence point
364  * to order the type check before the API call, discarding the "result" value of
365  * the type check and yielding the return value of the API call.
366  *
367  * C++ could be less of a headache than the C as we can leverage template
368  * functions. An advantage of template functions is that while their definition
369  * is driven by instantion, the definition does not appear at the source
370  * location of the instantiation, which gives it a great leg-up over the problems
371  * we have in the C path. However, the use of the msgbuf APIs in the test suite
372  * still makes things somewhat tricky, as the call-sites in the test suite are
373  * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
374  * takes both the object type and the required type as template arguments, and
375  * then define the object pointer parameter as `void *` for a call through to
376  * the appropriate msgbuf API. However, because the msgbuf API call-sites are
377  * encapsulated in gtest macros, use of commas in the template specification
378  * causes pre-processor confusion. In this way we're constrained to only one
379  * template argument per function.
380  *
381  * Implement the C++ path using template functions that take the destination
382  * object type as a template argument, while the name of the function symbols
383  * are derived from the required type. The manual implementations of these
384  * appear at the end of the header. The type safety is actually enforced
385  * by `static_assert()` this time, as we can use statements as we're not
386  * constrained to an expression in the templated function body.
387  *
388  * The invocations of pldm_msgbuf_extract_typecheck() typically result in
389  * double-evaluation of some arguments. We're not yet bothered by this for two
390  * reasons:
391  *
392  * 1. The nature of the current call-sites are such that there are no
393  *    argument expressions that result in undesirable side-effects
394  *
395  * 2. It's an API internal to the libpldm implementation, and we can fix things
396  *    whenever something crops up the violates the observation in 1.
397  */
398 #ifdef __cplusplus
399 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
400 	pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
401 #else
402 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
403 	(pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
404 #endif
405 
406 /**
407  * @brief pldm_msgbuf extractor for a uint8_t
408  *
409  * @param[in,out] ctx - pldm_msgbuf context for extractor
410  * @param[out] dst - destination of extracted value
411  *
412  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
413  * PLDM_ERROR_INVALID_LENGTH otherwise.
414  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
415  */
416 #define pldm_msgbuf_extract_uint8(ctx, dst)                                    \
417 	pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8,     \
418 				      dst, ctx, dst)
419 LIBPLDM_CC_NONNULL
420 LIBPLDM_CC_ALWAYS_INLINE int
421 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint8(struct pldm_msgbuf * ctx,void * dst)422 pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
423 {
424 	if (!ctx->cursor) {
425 		return pldm_msgbuf_status(ctx, EINVAL);
426 	}
427 
428 	if (ctx->remaining == INTMAX_MIN) {
429 		assert(ctx->remaining < 0);
430 		return pldm_msgbuf_status(ctx, EOVERFLOW);
431 	}
432 	ctx->remaining -= sizeof(uint8_t);
433 	assert(ctx->remaining >= 0);
434 	if (ctx->remaining < 0) {
435 		return pldm_msgbuf_status(ctx, EOVERFLOW);
436 	}
437 
438 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
439 
440 	ctx->cursor++;
441 	return 0;
442 }
443 
444 #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
445 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
446 				      ctx, dst)
447 LIBPLDM_CC_NONNULL
448 LIBPLDM_CC_ALWAYS_INLINE int
449 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int8(struct pldm_msgbuf * ctx,void * dst)450 pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
451 {
452 	if (!ctx->cursor) {
453 		return pldm_msgbuf_status(ctx, EINVAL);
454 	}
455 
456 	if (ctx->remaining == INTMAX_MIN) {
457 		assert(ctx->remaining < 0);
458 		return pldm_msgbuf_status(ctx, EOVERFLOW);
459 	}
460 	ctx->remaining -= sizeof(int8_t);
461 	assert(ctx->remaining >= 0);
462 	if (ctx->remaining < 0) {
463 		return pldm_msgbuf_status(ctx, EOVERFLOW);
464 	}
465 
466 	memcpy(dst, ctx->cursor, sizeof(int8_t));
467 	ctx->cursor++;
468 	return 0;
469 }
470 
471 #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
472 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
473 				      dst, ctx, dst)
474 LIBPLDM_CC_NONNULL
475 LIBPLDM_CC_ALWAYS_INLINE int
476 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint16(struct pldm_msgbuf * ctx,void * dst)477 pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst)
478 {
479 	uint16_t ldst;
480 
481 	if (!ctx->cursor) {
482 		return pldm_msgbuf_status(ctx, EINVAL);
483 	}
484 
485 	// Check for underflow while tracking the magnitude of the buffer overflow
486 	static_assert(
487 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
488 		sizeof(ldst) < INTMAX_MAX,
489 		"The following addition may not uphold the runtime assertion");
490 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
491 		assert(ctx->remaining < 0);
492 		return pldm_msgbuf_status(ctx, EOVERFLOW);
493 	}
494 
495 	// Check for buffer overflow. If we overflow, account for the request as
496 	// negative values in ctx->remaining. This way we can debug how far
497 	// we've overflowed.
498 	ctx->remaining -= sizeof(ldst);
499 
500 	// Prevent the access if it would overflow. First, assert so we blow up
501 	// the test suite right at the point of failure. However, cater to
502 	// -DNDEBUG by explicitly testing that the access is valid.
503 	assert(ctx->remaining >= 0);
504 	if (ctx->remaining < 0) {
505 		return pldm_msgbuf_status(ctx, EOVERFLOW);
506 	}
507 
508 	// Use memcpy() to have the compiler deal with any alignment
509 	// issues on the target architecture
510 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
511 
512 	// Only assign the target value once it's correctly decoded
513 	ldst = le16toh(ldst);
514 
515 	// Allow storing to unaligned
516 	memcpy(dst, &ldst, sizeof(ldst));
517 
518 	ctx->cursor += sizeof(ldst);
519 
520 	return 0;
521 }
522 
523 #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
524 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
525 				      dst, ctx, dst)
526 LIBPLDM_CC_NONNULL
527 LIBPLDM_CC_ALWAYS_INLINE int
528 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int16(struct pldm_msgbuf * ctx,void * dst)529 pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
530 {
531 	int16_t ldst;
532 
533 	if (!ctx->cursor) {
534 		return pldm_msgbuf_status(ctx, EINVAL);
535 	}
536 
537 	static_assert(
538 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
539 		sizeof(ldst) < INTMAX_MAX,
540 		"The following addition may not uphold the runtime assertion");
541 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
542 		assert(ctx->remaining < 0);
543 		return pldm_msgbuf_status(ctx, EOVERFLOW);
544 	}
545 	ctx->remaining -= sizeof(ldst);
546 	assert(ctx->remaining >= 0);
547 	if (ctx->remaining < 0) {
548 		return pldm_msgbuf_status(ctx, EOVERFLOW);
549 	}
550 
551 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
552 
553 	ldst = le16toh(ldst);
554 	memcpy(dst, &ldst, sizeof(ldst));
555 	ctx->cursor += sizeof(ldst);
556 
557 	return 0;
558 }
559 
560 #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
561 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
562 				      dst, ctx, dst)
563 LIBPLDM_CC_NONNULL
564 LIBPLDM_CC_ALWAYS_INLINE int
565 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_uint32(struct pldm_msgbuf * ctx,void * dst)566 pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst)
567 {
568 	uint32_t ldst;
569 
570 	if (!ctx->cursor) {
571 		return pldm_msgbuf_status(ctx, EINVAL);
572 	}
573 
574 	static_assert(
575 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
576 		sizeof(ldst) < INTMAX_MAX,
577 		"The following addition may not uphold the runtime assertion");
578 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
579 		assert(ctx->remaining < 0);
580 		return pldm_msgbuf_status(ctx, EOVERFLOW);
581 	}
582 	ctx->remaining -= sizeof(ldst);
583 	assert(ctx->remaining >= 0);
584 	if (ctx->remaining < 0) {
585 		return pldm_msgbuf_status(ctx, EOVERFLOW);
586 	}
587 
588 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
589 	ldst = le32toh(ldst);
590 	memcpy(dst, &ldst, sizeof(ldst));
591 	ctx->cursor += sizeof(ldst);
592 
593 	return 0;
594 }
595 
596 #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
597 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
598 				      dst, ctx, dst)
599 LIBPLDM_CC_NONNULL
600 LIBPLDM_CC_ALWAYS_INLINE int
601 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_int32(struct pldm_msgbuf * ctx,void * dst)602 pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
603 {
604 	int32_t ldst;
605 
606 	if (!ctx->cursor) {
607 		return pldm_msgbuf_status(ctx, EINVAL);
608 	}
609 
610 	static_assert(
611 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
612 		sizeof(ldst) < INTMAX_MAX,
613 		"The following addition may not uphold the runtime assertion");
614 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
615 		assert(ctx->remaining < 0);
616 		return pldm_msgbuf_status(ctx, EOVERFLOW);
617 	}
618 	ctx->remaining -= sizeof(ldst);
619 	assert(ctx->remaining >= 0);
620 	if (ctx->remaining < 0) {
621 		return pldm_msgbuf_status(ctx, EOVERFLOW);
622 	}
623 
624 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
625 	ldst = le32toh(ldst);
626 	memcpy(dst, &ldst, sizeof(ldst));
627 	ctx->cursor += sizeof(ldst);
628 
629 	return PLDM_SUCCESS;
630 }
631 
632 #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
633 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
634 				      dst, ctx, dst)
635 LIBPLDM_CC_NONNULL
636 LIBPLDM_CC_ALWAYS_INLINE int
637 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_real32(struct pldm_msgbuf * ctx,void * dst)638 pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst)
639 {
640 	uint32_t ldst;
641 
642 	static_assert(sizeof(real32_t) == sizeof(ldst),
643 		      "Mismatched type sizes for dst and ldst");
644 
645 	if (!ctx->cursor) {
646 		return pldm_msgbuf_status(ctx, EINVAL);
647 	}
648 
649 	static_assert(
650 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
651 		sizeof(ldst) < INTMAX_MAX,
652 		"The following addition may not uphold the runtime assertion");
653 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
654 		assert(ctx->remaining < 0);
655 		return pldm_msgbuf_status(ctx, EOVERFLOW);
656 	}
657 	ctx->remaining -= sizeof(ldst);
658 	assert(ctx->remaining >= 0);
659 	if (ctx->remaining < 0) {
660 		return pldm_msgbuf_status(ctx, EOVERFLOW);
661 	}
662 
663 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
664 	ldst = le32toh(ldst);
665 	memcpy(dst, &ldst, sizeof(ldst));
666 	ctx->cursor += sizeof(ldst);
667 
668 	return 0;
669 }
670 
671 /**
672  * Extract the field at the msgbuf cursor into the lvalue named by dst.
673  *
674  * @param ctx The msgbuf context object
675  * @param dst The lvalue into which the field at the msgbuf cursor should be
676  *            extracted
677  *
678  * @return PLDM_SUCCESS on success, otherwise another value on error
679  */
680 #define pldm_msgbuf_extract(ctx, dst)                                          \
681 	_Generic((dst),                                                        \
682 		uint8_t: pldm__msgbuf_extract_uint8,                           \
683 		int8_t: pldm__msgbuf_extract_int8,                             \
684 		uint16_t: pldm__msgbuf_extract_uint16,                         \
685 		int16_t: pldm__msgbuf_extract_int16,                           \
686 		uint32_t: pldm__msgbuf_extract_uint32,                         \
687 		int32_t: pldm__msgbuf_extract_int32,                           \
688 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
689 
690 /**
691  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
692  *
693  * @param ctx The msgbuf context object
694  * @param dst The pointer to the object into which the field at the msgbuf
695  *            cursor should be extracted
696  *
697  * @return PLDM_SUCCESS on success, otherwise another value on error
698  */
699 #define pldm_msgbuf_extract_p(ctx, dst)                                        \
700 	_Generic((dst),                                                        \
701 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
702 		int8_t *: pldm__msgbuf_extract_int8,                           \
703 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
704 		int16_t *: pldm__msgbuf_extract_int16,                         \
705 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
706 		int32_t *: pldm__msgbuf_extract_int32,                         \
707 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
708 
709 /**
710  * @ref pldm_msgbuf_extract_array
711  */
712 LIBPLDM_CC_NONNULL
713 LIBPLDM_CC_WARN_UNUSED_RESULT
714 LIBPLDM_CC_ALWAYS_INLINE int
715 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_extract_array_void(struct pldm_msgbuf * ctx,size_t count,void * dst,size_t dst_count)716 pldm__msgbuf_extract_array_void(struct pldm_msgbuf *ctx, size_t count,
717 				void *dst, size_t dst_count)
718 {
719 	if (!ctx->cursor || count > dst_count) {
720 		return pldm_msgbuf_status(ctx, EINVAL);
721 	}
722 
723 	if (!count) {
724 		return 0;
725 	}
726 
727 #if INTMAX_MAX < SIZE_MAX
728 	if (count > INTMAX_MAX) {
729 		return pldm_msgbuf_status(ctx, EOVERFLOW);
730 	}
731 #endif
732 
733 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
734 		return pldm_msgbuf_status(ctx, EOVERFLOW);
735 	}
736 	ctx->remaining -= (intmax_t)count;
737 	assert(ctx->remaining >= 0);
738 	if (ctx->remaining < 0) {
739 		return pldm_msgbuf_status(ctx, EOVERFLOW);
740 	}
741 
742 	memcpy(dst, ctx->cursor, count);
743 	ctx->cursor += count;
744 
745 	return 0;
746 }
747 
748 /**
749  * @ref pldm_msgbuf_extract_array
750  */
751 LIBPLDM_CC_NONNULL
752 LIBPLDM_CC_WARN_UNUSED_RESULT
753 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_extract_array_char(struct pldm_msgbuf * ctx,size_t count,char * dst,size_t dst_count)754 pldm_msgbuf_extract_array_char(struct pldm_msgbuf *ctx, size_t count, char *dst,
755 			       size_t dst_count)
756 {
757 	return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count);
758 }
759 
760 /**
761  * @ref pldm_msgbuf_extract_array
762  */
763 LIBPLDM_CC_NONNULL
764 LIBPLDM_CC_WARN_UNUSED_RESULT
765 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf * ctx,size_t count,uint8_t * dst,size_t dst_count)766 pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, size_t count,
767 				uint8_t *dst, size_t dst_count)
768 {
769 	return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count);
770 }
771 
772 /**
773  * Extract an array of data from the msgbuf instance
774  *
775  * @param ctx - The msgbuf instance from which to extract an array of data
776  * @param count - The number of array elements to extract
777  * @param dst - The array object into which elements from @p ctx should be
778                 extracted
779  * @param dst_count - The maximum number of elements to place into @p dst
780  *
781  * Note that both @p count and @p dst_count can only be counted by `sizeof` for
782  * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number
783  * of array elements and _not_ the object size of the array.
784  */
785 #define pldm_msgbuf_extract_array(ctx, count, dst, dst_count)                  \
786 	_Generic((*(dst)),                                                     \
787 		uint8_t: pldm_msgbuf_extract_array_uint8,                      \
788 		char: pldm_msgbuf_extract_array_char)(ctx, count, dst,         \
789 						      dst_count)
790 
791 LIBPLDM_CC_NONNULL
pldm_msgbuf_insert_uint32(struct pldm_msgbuf * ctx,const uint32_t src)792 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
793 						       const uint32_t src)
794 {
795 	uint32_t val = htole32(src);
796 
797 	if (!ctx->cursor) {
798 		return pldm_msgbuf_status(ctx, EINVAL);
799 	}
800 
801 	static_assert(
802 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
803 		sizeof(src) < INTMAX_MAX,
804 		"The following addition may not uphold the runtime assertion");
805 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
806 		assert(ctx->remaining < 0);
807 		return pldm_msgbuf_status(ctx, EOVERFLOW);
808 	}
809 	ctx->remaining -= sizeof(src);
810 	assert(ctx->remaining >= 0);
811 	if (ctx->remaining < 0) {
812 		return pldm_msgbuf_status(ctx, EOVERFLOW);
813 	}
814 
815 	memcpy(ctx->cursor, &val, sizeof(val));
816 	ctx->cursor += sizeof(src);
817 
818 	return 0;
819 }
820 
821 LIBPLDM_CC_NONNULL
pldm_msgbuf_insert_uint16(struct pldm_msgbuf * ctx,const uint16_t src)822 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
823 						       const uint16_t src)
824 {
825 	uint16_t val = htole16(src);
826 
827 	if (!ctx->cursor) {
828 		return pldm_msgbuf_status(ctx, EINVAL);
829 	}
830 
831 	static_assert(
832 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
833 		sizeof(src) < INTMAX_MAX,
834 		"The following addition may not uphold the runtime assertion");
835 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
836 		assert(ctx->remaining < 0);
837 		return pldm_msgbuf_status(ctx, EOVERFLOW);
838 	}
839 	ctx->remaining -= sizeof(src);
840 	assert(ctx->remaining >= 0);
841 	if (ctx->remaining < 0) {
842 		return pldm_msgbuf_status(ctx, EOVERFLOW);
843 	}
844 
845 	memcpy(ctx->cursor, &val, sizeof(val));
846 	ctx->cursor += sizeof(src);
847 
848 	return 0;
849 }
850 
851 LIBPLDM_CC_NONNULL
pldm_msgbuf_insert_uint8(struct pldm_msgbuf * ctx,const uint8_t src)852 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
853 						      const uint8_t src)
854 {
855 	if (!ctx->cursor) {
856 		return pldm_msgbuf_status(ctx, EINVAL);
857 	}
858 
859 	static_assert(
860 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
861 		sizeof(src) < INTMAX_MAX,
862 		"The following addition may not uphold the runtime assertion");
863 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
864 		assert(ctx->remaining < 0);
865 		return pldm_msgbuf_status(ctx, EOVERFLOW);
866 	}
867 	ctx->remaining -= sizeof(src);
868 	assert(ctx->remaining >= 0);
869 	if (ctx->remaining < 0) {
870 		return pldm_msgbuf_status(ctx, EOVERFLOW);
871 	}
872 
873 	memcpy(ctx->cursor, &src, sizeof(src));
874 	ctx->cursor += sizeof(src);
875 
876 	return 0;
877 }
878 
879 LIBPLDM_CC_NONNULL
pldm_msgbuf_insert_int32(struct pldm_msgbuf * ctx,const int32_t src)880 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
881 						      const int32_t src)
882 {
883 	int32_t val = htole32(src);
884 
885 	if (!ctx->cursor) {
886 		return pldm_msgbuf_status(ctx, EINVAL);
887 	}
888 
889 	static_assert(
890 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
891 		sizeof(src) < INTMAX_MAX,
892 		"The following addition may not uphold the runtime assertion");
893 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
894 		assert(ctx->remaining < 0);
895 		return pldm_msgbuf_status(ctx, EOVERFLOW);
896 	}
897 	ctx->remaining -= sizeof(src);
898 	assert(ctx->remaining >= 0);
899 	if (ctx->remaining < 0) {
900 		return pldm_msgbuf_status(ctx, EOVERFLOW);
901 	}
902 
903 	memcpy(ctx->cursor, &val, sizeof(val));
904 	ctx->cursor += sizeof(src);
905 
906 	return 0;
907 }
908 
909 LIBPLDM_CC_NONNULL
pldm_msgbuf_insert_int16(struct pldm_msgbuf * ctx,const int16_t src)910 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
911 						      const int16_t src)
912 {
913 	int16_t val = htole16(src);
914 
915 	if (!ctx->cursor) {
916 		return pldm_msgbuf_status(ctx, EINVAL);
917 	}
918 
919 	static_assert(
920 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
921 		sizeof(src) < INTMAX_MAX,
922 		"The following addition may not uphold the runtime assertion");
923 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
924 		assert(ctx->remaining < 0);
925 		return pldm_msgbuf_status(ctx, EOVERFLOW);
926 	}
927 	ctx->remaining -= sizeof(src);
928 	assert(ctx->remaining >= 0);
929 	if (ctx->remaining < 0) {
930 		return pldm_msgbuf_status(ctx, EOVERFLOW);
931 	}
932 
933 	memcpy(ctx->cursor, &val, sizeof(val));
934 	ctx->cursor += sizeof(src);
935 
936 	return 0;
937 }
938 
939 LIBPLDM_CC_NONNULL
pldm_msgbuf_insert_int8(struct pldm_msgbuf * ctx,const int8_t src)940 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
941 						     const int8_t src)
942 {
943 	if (!ctx->cursor) {
944 		return pldm_msgbuf_status(ctx, EINVAL);
945 	}
946 
947 	static_assert(
948 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
949 		sizeof(src) < INTMAX_MAX,
950 		"The following addition may not uphold the runtime assertion");
951 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
952 		assert(ctx->remaining < 0);
953 		return pldm_msgbuf_status(ctx, EOVERFLOW);
954 	}
955 	ctx->remaining -= sizeof(src);
956 	assert(ctx->remaining >= 0);
957 	if (ctx->remaining < 0) {
958 		return pldm_msgbuf_status(ctx, EOVERFLOW);
959 	}
960 
961 	memcpy(ctx->cursor, &src, sizeof(src));
962 	ctx->cursor += sizeof(src);
963 
964 	return 0;
965 }
966 
967 #define pldm_msgbuf_insert(dst, src)                                           \
968 	_Generic((src),                                                        \
969 		uint8_t: pldm_msgbuf_insert_uint8,                             \
970 		int8_t: pldm_msgbuf_insert_int8,                               \
971 		uint16_t: pldm_msgbuf_insert_uint16,                           \
972 		int16_t: pldm_msgbuf_insert_int16,                             \
973 		uint32_t: pldm_msgbuf_insert_uint32,                           \
974 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
975 
976 /**
977  * @ref pldm_msgbuf_insert_array
978  */
979 LIBPLDM_CC_NONNULL
980 LIBPLDM_CC_WARN_UNUSED_RESULT
981 LIBPLDM_CC_ALWAYS_INLINE int
982 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_insert_array_void(struct pldm_msgbuf * ctx,size_t count,const void * src,size_t src_count)983 pldm__msgbuf_insert_array_void(struct pldm_msgbuf *ctx, size_t count,
984 			       const void *src, size_t src_count)
985 {
986 	if (!ctx->cursor || count > src_count) {
987 		return pldm_msgbuf_status(ctx, EINVAL);
988 	}
989 
990 	if (!count) {
991 		return 0;
992 	}
993 
994 #if INTMAX_MAX < SIZE_MAX
995 	if (count > INTMAX_MAX) {
996 		return pldm_msgbuf_status(ctx, EOVERFLOW);
997 	}
998 #endif
999 
1000 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
1001 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1002 	}
1003 	ctx->remaining -= (intmax_t)count;
1004 	assert(ctx->remaining >= 0);
1005 	if (ctx->remaining < 0) {
1006 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1007 	}
1008 
1009 	memcpy(ctx->cursor, src, count);
1010 	ctx->cursor += count;
1011 
1012 	return 0;
1013 }
1014 
1015 /**
1016  * @ref pldm_msgbuf_insert_array
1017  */
1018 LIBPLDM_CC_NONNULL
1019 LIBPLDM_CC_WARN_UNUSED_RESULT
1020 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_array_char(struct pldm_msgbuf * ctx,size_t count,const char * src,size_t src_count)1021 pldm_msgbuf_insert_array_char(struct pldm_msgbuf *ctx, size_t count,
1022 			      const char *src, size_t src_count)
1023 {
1024 	return pldm__msgbuf_insert_array_void(ctx, count, src, src_count);
1025 }
1026 
1027 /**
1028  * @ref pldm_msgbuf_insert_array
1029  */
1030 LIBPLDM_CC_NONNULL
1031 LIBPLDM_CC_WARN_UNUSED_RESULT
1032 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf * ctx,size_t count,const uint8_t * src,size_t src_count)1033 pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, size_t count,
1034 			       const uint8_t *src, size_t src_count)
1035 {
1036 	return pldm__msgbuf_insert_array_void(ctx, count, src, src_count);
1037 }
1038 
1039 /**
1040  * Insert an array of data into the msgbuf instance
1041  *
1042  * @param ctx - The msgbuf instance into which the array of data should be
1043  *              inserted
1044  * @param count - The number of array elements to insert
1045  * @param src - The array object from which elements should be inserted into
1046                 @p ctx
1047  * @param src_count - The maximum number of elements to insert from @p src
1048  *
1049  * Note that both @p count and @p src_count can only be counted by `sizeof` for
1050  * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number
1051  * of array elements and _not_ the object size of the array.
1052  */
1053 #define pldm_msgbuf_insert_array(dst, count, src, src_count)                   \
1054 	_Generic((*(src)),                                                     \
1055 		uint8_t: pldm_msgbuf_insert_array_uint8,                       \
1056 		char: pldm_msgbuf_insert_array_char)(dst, count, src,          \
1057 						     src_count)
1058 
1059 LIBPLDM_CC_NONNULL_ARGS(1)
pldm_msgbuf_span_required(struct pldm_msgbuf * ctx,size_t required,void ** cursor)1060 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
1061 						       size_t required,
1062 						       void **cursor)
1063 {
1064 	if (!ctx->cursor || (cursor && *cursor)) {
1065 		return pldm_msgbuf_status(ctx, EINVAL);
1066 	}
1067 
1068 #if INTMAX_MAX < SIZE_MAX
1069 	if (required > INTMAX_MAX) {
1070 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1071 	}
1072 #endif
1073 
1074 	if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
1075 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1076 	}
1077 	ctx->remaining -= (intmax_t)required;
1078 	assert(ctx->remaining >= 0);
1079 	if (ctx->remaining < 0) {
1080 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1081 	}
1082 
1083 	if (cursor) {
1084 		*cursor = ctx->cursor;
1085 	}
1086 	ctx->cursor += required;
1087 
1088 	return 0;
1089 }
1090 
1091 LIBPLDM_CC_NONNULL_ARGS(1)
1092 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_span_string_ascii(struct pldm_msgbuf * ctx,void ** cursor,size_t * length)1093 pldm_msgbuf_span_string_ascii(struct pldm_msgbuf *ctx, void **cursor,
1094 			      size_t *length)
1095 {
1096 	intmax_t measured;
1097 
1098 	if (!ctx->cursor || (cursor && *cursor)) {
1099 		return pldm_msgbuf_status(ctx, EINVAL);
1100 	}
1101 
1102 	if (ctx->remaining < 0) {
1103 		/* Tracking the amount of overflow gets disturbed here */
1104 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1105 	}
1106 
1107 	measured = (intmax_t)strnlen((const char *)ctx->cursor, ctx->remaining);
1108 	if (measured == ctx->remaining) {
1109 		/*
1110 		 * We have hit the end of the buffer prior to the NUL terminator.
1111 		 * Optimistically, the NUL terminator was one-beyond-the-end. Setting
1112 		 * ctx->remaining negative ensures the `pldm_msgbuf_destroy*()` APIs also
1113 		 * return an error.
1114 		 */
1115 		ctx->remaining = -1;
1116 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1117 	}
1118 
1119 	/* Include the NUL terminator in the span length, as spans are opaque */
1120 	measured++;
1121 
1122 	if (ctx->remaining < INTMAX_MIN + measured) {
1123 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1124 	}
1125 
1126 	ctx->remaining -= measured;
1127 	assert(ctx->remaining >= 0);
1128 	if (ctx->remaining < 0) {
1129 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1130 	}
1131 
1132 	if (cursor) {
1133 		*cursor = ctx->cursor;
1134 	}
1135 
1136 	ctx->cursor += measured;
1137 
1138 	if (length) {
1139 		*length = measured;
1140 	}
1141 
1142 	return 0;
1143 }
1144 
1145 LIBPLDM_CC_NONNULL_ARGS(1)
1146 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_span_string_utf16(struct pldm_msgbuf * ctx,void ** cursor,size_t * length)1147 pldm_msgbuf_span_string_utf16(struct pldm_msgbuf *ctx, void **cursor,
1148 			      size_t *length)
1149 {
1150 	static const char16_t term = 0;
1151 	ptrdiff_t measured;
1152 	void *end;
1153 
1154 	if (!ctx->cursor || (cursor && *cursor)) {
1155 		return pldm_msgbuf_status(ctx, EINVAL);
1156 	}
1157 
1158 	if (ctx->remaining < 0) {
1159 		/* Tracking the amount of overflow gets disturbed here */
1160 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1161 	}
1162 
1163 	/*
1164 	 * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do
1165 	 * not form a UTF16 NUL _code-point_ due to alignment with respect to the
1166 	 * start of the string
1167 	 */
1168 	end = ctx->cursor;
1169 	do {
1170 		if (end != ctx->cursor) {
1171 			/*
1172 			 * If we've looped we've found a relatively-unaligned NUL code-point.
1173 			 * Scan again from a relatively-aligned start point.
1174 			 */
1175 			end = (char *)end + 1;
1176 		}
1177 		measured = (char *)end - (char *)ctx->cursor;
1178 		end = memmem(end, ctx->remaining - measured, &term,
1179 			     sizeof(term));
1180 	} while (end && ((uintptr_t)end & 1) != ((uintptr_t)ctx->cursor & 1));
1181 
1182 	if (!end) {
1183 		/*
1184 		 * Optimistically, the last required pattern byte was one beyond the end of
1185 		 * the buffer. Setting ctx->remaining negative ensures the
1186 		 * `pldm_msgbuf_destroy*()` APIs also return an error.
1187 		 */
1188 		ctx->remaining = -1;
1189 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1190 	}
1191 
1192 	end = (char *)end + sizeof(char16_t);
1193 	measured = (char *)end - (char *)ctx->cursor;
1194 
1195 #if INTMAX_MAX < PTRDIFF_MAX
1196 	if (measured >= INTMAX_MAX) {
1197 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1198 	}
1199 #endif
1200 
1201 	if (ctx->remaining < INTMAX_MIN + (intmax_t)measured) {
1202 		assert(ctx->remaining < 0);
1203 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1204 	}
1205 
1206 	ctx->remaining -= (intmax_t)measured;
1207 	assert(ctx->remaining >= 0);
1208 	if (ctx->remaining < 0) {
1209 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1210 	}
1211 
1212 	if (cursor) {
1213 		*cursor = ctx->cursor;
1214 	}
1215 
1216 	ctx->cursor += measured;
1217 
1218 	if (length) {
1219 		*length = (size_t)measured;
1220 	}
1221 
1222 	return 0;
1223 }
1224 
1225 LIBPLDM_CC_NONNULL
1226 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_span_remaining(struct pldm_msgbuf * ctx,void ** cursor,size_t * len)1227 pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
1228 {
1229 	if (!ctx->cursor || *cursor) {
1230 		return pldm_msgbuf_status(ctx, EINVAL);
1231 	}
1232 
1233 	assert(ctx->remaining >= 0);
1234 	if (ctx->remaining < 0) {
1235 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1236 	}
1237 
1238 	*cursor = ctx->cursor;
1239 	ctx->cursor += ctx->remaining;
1240 	*len = ctx->remaining;
1241 	ctx->remaining = 0;
1242 
1243 	return 0;
1244 }
1245 
1246 /**
1247  * @brief pldm_msgbuf copy data between two msg buffers
1248  *
1249  * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1250  * @param[in,out] dst - destination of copy from source
1251  * @param[in] size - size of data to be copied
1252  * @param[in] description - description of data copied
1253  *
1254  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1255  * PLDM_ERROR_INVALID_LENGTH otherwise.
1256  * PLDM_ERROR_INVALID_DATA if input is invalid
1257  */
1258 #define pldm_msgbuf_copy(dst, src, type, name)                                 \
1259 	pldm__msgbuf_copy(dst, src, sizeof(type), #name)
1260 LIBPLDM_CC_NONNULL
1261 LIBPLDM_CC_ALWAYS_INLINE int
1262 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
pldm__msgbuf_copy(struct pldm_msgbuf * dst,struct pldm_msgbuf * src,size_t size,const char * description LIBPLDM_CC_UNUSED)1263 pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1264 		  const char *description LIBPLDM_CC_UNUSED)
1265 {
1266 	assert(src->mode == dst->mode);
1267 
1268 	if (!src->cursor || !dst->cursor) {
1269 		return pldm_msgbuf_status(dst, EINVAL);
1270 	}
1271 
1272 #if INTMAX_MAX < SIZE_MAX
1273 	if (size > INTMAX_MAX) {
1274 		return pldm_msgbuf_status(dst, EOVERFLOW);
1275 	}
1276 #endif
1277 
1278 	if (src->remaining < INTMAX_MIN + (intmax_t)size) {
1279 		return pldm_msgbuf_status(dst, EOVERFLOW);
1280 	}
1281 
1282 	if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
1283 		return pldm_msgbuf_status(dst, EOVERFLOW);
1284 	}
1285 
1286 	src->remaining -= (intmax_t)size;
1287 	assert(src->remaining >= 0);
1288 	if (src->remaining < 0) {
1289 		return pldm_msgbuf_status(dst, EOVERFLOW);
1290 	}
1291 
1292 	dst->remaining -= (intmax_t)size;
1293 	assert(dst->remaining >= 0);
1294 	if (dst->remaining < 0) {
1295 		return pldm_msgbuf_status(dst, EOVERFLOW);
1296 	}
1297 
1298 	memcpy(dst->cursor, src->cursor, size);
1299 	src->cursor += size;
1300 	dst->cursor += size;
1301 
1302 	return 0;
1303 }
1304 
1305 LIBPLDM_CC_NONNULL
1306 LIBPLDM_CC_WARN_UNUSED_RESULT
1307 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf * dst,struct pldm_msgbuf * src)1308 pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1309 {
1310 	void *ascii = NULL;
1311 	size_t len = 0;
1312 	int rc;
1313 
1314 	rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len);
1315 	if (rc < 0) {
1316 		return rc;
1317 	}
1318 
1319 	return pldm__msgbuf_insert_array_void(dst, len, ascii, len);
1320 }
1321 
1322 LIBPLDM_CC_NONNULL
1323 LIBPLDM_CC_WARN_UNUSED_RESULT
1324 LIBPLDM_CC_ALWAYS_INLINE int
pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf * dst,struct pldm_msgbuf * src)1325 pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1326 {
1327 	void *utf16 = NULL;
1328 	size_t len = 0;
1329 	int rc;
1330 
1331 	rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len);
1332 	if (rc < 0) {
1333 		return rc;
1334 	}
1335 
1336 	return pldm__msgbuf_insert_array_void(dst, len, utf16, len);
1337 }
1338 
1339 #ifdef __cplusplus
1340 }
1341 #endif
1342 
1343 #ifdef __cplusplus
1344 #include <type_traits>
1345 
1346 template <typename T>
pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf * ctx,void * buf)1347 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
1348 						void *buf)
1349 {
1350 	static_assert(std::is_same<uint8_t *, T>::value);
1351 	return pldm__msgbuf_extract_uint8(ctx, buf);
1352 }
1353 
1354 template <typename T>
pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf * ctx,void * buf)1355 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
1356 					       void *buf)
1357 {
1358 	static_assert(std::is_same<int8_t *, T>::value);
1359 	return pldm__msgbuf_extract_int8(ctx, buf);
1360 }
1361 
1362 template <typename T>
pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf * ctx,void * buf)1363 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
1364 						 void *buf)
1365 {
1366 	static_assert(std::is_same<uint16_t *, T>::value);
1367 	return pldm__msgbuf_extract_uint16(ctx, buf);
1368 }
1369 
1370 template <typename T>
pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf * ctx,void * buf)1371 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
1372 						void *buf)
1373 {
1374 	static_assert(std::is_same<int16_t *, T>::value);
1375 	return pldm__msgbuf_extract_int16(ctx, buf);
1376 }
1377 
1378 template <typename T>
pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf * ctx,void * buf)1379 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
1380 						 void *buf)
1381 {
1382 	static_assert(std::is_same<uint32_t *, T>::value);
1383 	return pldm__msgbuf_extract_uint32(ctx, buf);
1384 }
1385 
1386 template <typename T>
pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf * ctx,void * buf)1387 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
1388 						void *buf)
1389 {
1390 	static_assert(std::is_same<int32_t *, T>::value);
1391 	return pldm__msgbuf_extract_int32(ctx, buf);
1392 }
1393 
1394 template <typename T>
pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf * ctx,void * buf)1395 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
1396 						 void *buf)
1397 {
1398 	static_assert(std::is_same<real32_t *, T>::value);
1399 	return pldm__msgbuf_extract_real32(ctx, buf);
1400 }
1401 #endif
1402 
1403 #endif /* BUF_H */
1404