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