xref: /openbmc/libpldm/src/msgbuf.h (revision 830c1eb49c8cc664b4f61c92fc648c152858ab4a)
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)
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_t)buf + len < 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
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
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
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
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)
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_MIN) {
316 		assert(ctx->remaining < 0);
317 		return -EOVERFLOW;
318 	}
319 	ctx->remaining -= sizeof(uint8_t);
320 	assert(ctx->remaining >= 0);
321 	if (ctx->remaining < 0) {
322 		return -EOVERFLOW;
323 	}
324 
325 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
326 
327 	ctx->cursor++;
328 	return 0;
329 }
330 
331 #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
332 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
333 				      ctx, (void *)&(dst))
334 LIBPLDM_CC_NONNULL
335 LIBPLDM_CC_ALWAYS_INLINE int
336 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
337 pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
338 {
339 	if (!ctx->cursor) {
340 		return -EINVAL;
341 	}
342 
343 	if (ctx->remaining == INTMAX_MIN) {
344 		assert(ctx->remaining < 0);
345 		return -EOVERFLOW;
346 	}
347 	ctx->remaining -= sizeof(int8_t);
348 	assert(ctx->remaining >= 0);
349 	if (ctx->remaining < 0) {
350 		return -EOVERFLOW;
351 	}
352 
353 	memcpy(dst, ctx->cursor, sizeof(int8_t));
354 	ctx->cursor++;
355 	return 0;
356 }
357 
358 #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
359 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
360 				      dst, ctx, (void *)&(dst))
361 LIBPLDM_CC_NONNULL
362 LIBPLDM_CC_ALWAYS_INLINE int
363 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
364 pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst)
365 {
366 	uint16_t ldst;
367 
368 	if (!ctx->cursor) {
369 		return -EINVAL;
370 	}
371 
372 	// Check for underflow while tracking the magnitude of the buffer overflow
373 	static_assert(
374 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
375 		sizeof(ldst) < INTMAX_MAX,
376 		"The following addition may not uphold the runtime assertion");
377 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
378 		assert(ctx->remaining < 0);
379 		return -EOVERFLOW;
380 	}
381 
382 	// Check for buffer overflow. If we overflow, account for the request as
383 	// negative values in ctx->remaining. This way we can debug how far
384 	// we've overflowed.
385 	ctx->remaining -= sizeof(ldst);
386 
387 	// Prevent the access if it would overflow. First, assert so we blow up
388 	// the test suite right at the point of failure. However, cater to
389 	// -DNDEBUG by explicitly testing that the access is valid.
390 	assert(ctx->remaining >= 0);
391 	if (ctx->remaining < 0) {
392 		return -EOVERFLOW;
393 	}
394 
395 	// Use memcpy() to have the compiler deal with any alignment
396 	// issues on the target architecture
397 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
398 
399 	// Only assign the target value once it's correctly decoded
400 	ldst = le16toh(ldst);
401 
402 	// Allow storing to unaligned
403 	memcpy(dst, &ldst, sizeof(ldst));
404 
405 	ctx->cursor += sizeof(ldst);
406 
407 	return 0;
408 }
409 
410 #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
411 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
412 				      dst, ctx, (void *)&(dst))
413 LIBPLDM_CC_NONNULL
414 LIBPLDM_CC_ALWAYS_INLINE int
415 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
416 pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
417 {
418 	int16_t ldst;
419 
420 	if (!ctx->cursor) {
421 		return -EINVAL;
422 	}
423 
424 	static_assert(
425 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
426 		sizeof(ldst) < INTMAX_MAX,
427 		"The following addition may not uphold the runtime assertion");
428 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
429 		assert(ctx->remaining < 0);
430 		return -EOVERFLOW;
431 	}
432 	ctx->remaining -= sizeof(ldst);
433 	assert(ctx->remaining >= 0);
434 	if (ctx->remaining < 0) {
435 		return -EOVERFLOW;
436 	}
437 
438 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
439 
440 	ldst = le16toh(ldst);
441 	memcpy(dst, &ldst, sizeof(ldst));
442 	ctx->cursor += sizeof(ldst);
443 
444 	return 0;
445 }
446 
447 #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
448 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
449 				      dst, ctx, (void *)&(dst))
450 LIBPLDM_CC_NONNULL
451 LIBPLDM_CC_ALWAYS_INLINE int
452 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
453 pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst)
454 {
455 	uint32_t ldst;
456 
457 	if (!ctx->cursor) {
458 		return -EINVAL;
459 	}
460 
461 	static_assert(
462 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
463 		sizeof(ldst) < INTMAX_MAX,
464 		"The following addition may not uphold the runtime assertion");
465 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
466 		assert(ctx->remaining < 0);
467 		return -EOVERFLOW;
468 	}
469 	ctx->remaining -= sizeof(ldst);
470 	assert(ctx->remaining >= 0);
471 	if (ctx->remaining < 0) {
472 		return -EOVERFLOW;
473 	}
474 
475 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
476 	ldst = le32toh(ldst);
477 	memcpy(dst, &ldst, sizeof(ldst));
478 	ctx->cursor += sizeof(ldst);
479 
480 	return 0;
481 }
482 
483 #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
484 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
485 				      dst, ctx, (void *)&(dst))
486 LIBPLDM_CC_NONNULL
487 LIBPLDM_CC_ALWAYS_INLINE int
488 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
489 pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
490 {
491 	int32_t ldst;
492 
493 	if (!ctx->cursor) {
494 		return -EINVAL;
495 	}
496 
497 	static_assert(
498 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
499 		sizeof(ldst) < INTMAX_MAX,
500 		"The following addition may not uphold the runtime assertion");
501 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
502 		assert(ctx->remaining < 0);
503 		return -EOVERFLOW;
504 	}
505 	ctx->remaining -= sizeof(ldst);
506 	assert(ctx->remaining >= 0);
507 	if (ctx->remaining < 0) {
508 		return -EOVERFLOW;
509 	}
510 
511 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
512 	ldst = le32toh(ldst);
513 	memcpy(dst, &ldst, sizeof(ldst));
514 	ctx->cursor += sizeof(ldst);
515 
516 	return PLDM_SUCCESS;
517 }
518 
519 #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
520 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
521 				      dst, ctx, (void *)&(dst))
522 LIBPLDM_CC_NONNULL
523 LIBPLDM_CC_ALWAYS_INLINE int
524 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
525 pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst)
526 {
527 	uint32_t ldst;
528 
529 	static_assert(sizeof(real32_t) == sizeof(ldst),
530 		      "Mismatched type sizes for dst and ldst");
531 
532 	if (!ctx->cursor) {
533 		return -EINVAL;
534 	}
535 
536 	static_assert(
537 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
538 		sizeof(ldst) < INTMAX_MAX,
539 		"The following addition may not uphold the runtime assertion");
540 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
541 		assert(ctx->remaining < 0);
542 		return -EOVERFLOW;
543 	}
544 	ctx->remaining -= sizeof(ldst);
545 	assert(ctx->remaining >= 0);
546 	if (ctx->remaining < 0) {
547 		return -EOVERFLOW;
548 	}
549 
550 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
551 	ldst = le32toh(ldst);
552 	memcpy(dst, &ldst, sizeof(ldst));
553 	ctx->cursor += sizeof(ldst);
554 
555 	return 0;
556 }
557 
558 /**
559  * Extract the field at the msgbuf cursor into the lvalue named by dst.
560  *
561  * @param ctx The msgbuf context object
562  * @param dst The lvalue into which the field at the msgbuf cursor should be
563  *            extracted
564  *
565  * @return PLDM_SUCCESS on success, otherwise another value on error
566  */
567 #define pldm_msgbuf_extract(ctx, dst)                                          \
568 	_Generic((dst),                                                        \
569 		uint8_t: pldm__msgbuf_extract_uint8,                           \
570 		int8_t: pldm__msgbuf_extract_int8,                             \
571 		uint16_t: pldm__msgbuf_extract_uint16,                         \
572 		int16_t: pldm__msgbuf_extract_int16,                           \
573 		uint32_t: pldm__msgbuf_extract_uint32,                         \
574 		int32_t: pldm__msgbuf_extract_int32,                           \
575 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
576 
577 /**
578  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
579  *
580  * @param ctx The msgbuf context object
581  * @param dst The pointer to the object into which the field at the msgbuf
582  *            cursor should be extracted
583  *
584  * @return PLDM_SUCCESS on success, otherwise another value on error
585  */
586 #define pldm_msgbuf_extract_p(ctx, dst)                                        \
587 	_Generic((dst),                                                        \
588 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
589 		int8_t *: pldm__msgbuf_extract_int8,                           \
590 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
591 		int16_t *: pldm__msgbuf_extract_int16,                         \
592 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
593 		int32_t *: pldm__msgbuf_extract_int32,                         \
594 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
595 
596 /**
597  * @ref pldm_msgbuf_extract_array
598  */
599 LIBPLDM_CC_NONNULL
600 LIBPLDM_CC_WARN_UNUSED_RESULT
601 LIBPLDM_CC_ALWAYS_INLINE int
602 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
603 pldm__msgbuf_extract_array_void(struct pldm_msgbuf *ctx, size_t count,
604 				void *dst, size_t dst_count)
605 {
606 	if (!ctx->cursor || count > dst_count) {
607 		return -EINVAL;
608 	}
609 
610 	if (!count) {
611 		return 0;
612 	}
613 
614 #if INTMAX_MAX < SIZE_MAX
615 	if (count > INTMAX_MAX) {
616 		return -EOVERFLOW;
617 	}
618 #endif
619 
620 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
621 		return -EOVERFLOW;
622 	}
623 	ctx->remaining -= (intmax_t)count;
624 	assert(ctx->remaining >= 0);
625 	if (ctx->remaining < 0) {
626 		return -EOVERFLOW;
627 	}
628 
629 	memcpy(dst, ctx->cursor, count);
630 	ctx->cursor += count;
631 
632 	return 0;
633 }
634 
635 /**
636  * @ref pldm_msgbuf_extract_array
637  */
638 LIBPLDM_CC_NONNULL
639 LIBPLDM_CC_WARN_UNUSED_RESULT
640 LIBPLDM_CC_ALWAYS_INLINE int
641 pldm_msgbuf_extract_array_char(struct pldm_msgbuf *ctx, size_t count, char *dst,
642 			       size_t dst_count)
643 {
644 	return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count);
645 }
646 
647 /**
648  * @ref pldm_msgbuf_extract_array
649  */
650 LIBPLDM_CC_NONNULL
651 LIBPLDM_CC_WARN_UNUSED_RESULT
652 LIBPLDM_CC_ALWAYS_INLINE int
653 pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, size_t count,
654 				uint8_t *dst, size_t dst_count)
655 {
656 	return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count);
657 }
658 
659 /**
660  * Extract an array of data from the msgbuf instance
661  *
662  * @param ctx - The msgbuf instance from which to extract an array of data
663  * @param count - The number of array elements to extract
664  * @param dst - The array object into which elements from @p ctx should be
665                 extracted
666  * @param dst_count - The maximum number of elements to place into @p dst
667  *
668  * Note that both @p count and @p dst_count can only be counted by `sizeof` for
669  * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number
670  * of array elements and _not_ the object size of the array.
671  */
672 #define pldm_msgbuf_extract_array(ctx, count, dst, dst_count)                  \
673 	_Generic((*(dst)),                                                     \
674 		uint8_t: pldm_msgbuf_extract_array_uint8,                      \
675 		char: pldm_msgbuf_extract_array_char)(ctx, count, dst,         \
676 						      dst_count)
677 
678 LIBPLDM_CC_NONNULL
679 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
680 						       const uint32_t src)
681 {
682 	uint32_t val = htole32(src);
683 
684 	if (!ctx->cursor) {
685 		return -EINVAL;
686 	}
687 
688 	static_assert(
689 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
690 		sizeof(src) < INTMAX_MAX,
691 		"The following addition may not uphold the runtime assertion");
692 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
693 		assert(ctx->remaining < 0);
694 		return -EOVERFLOW;
695 	}
696 	ctx->remaining -= sizeof(src);
697 	assert(ctx->remaining >= 0);
698 	if (ctx->remaining < 0) {
699 		return -EOVERFLOW;
700 	}
701 
702 	memcpy(ctx->cursor, &val, sizeof(val));
703 	ctx->cursor += sizeof(src);
704 
705 	return 0;
706 }
707 
708 LIBPLDM_CC_NONNULL
709 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
710 						       const uint16_t src)
711 {
712 	uint16_t val = htole16(src);
713 
714 	if (!ctx->cursor) {
715 		return -EINVAL;
716 	}
717 
718 	static_assert(
719 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
720 		sizeof(src) < INTMAX_MAX,
721 		"The following addition may not uphold the runtime assertion");
722 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
723 		assert(ctx->remaining < 0);
724 		return -EOVERFLOW;
725 	}
726 	ctx->remaining -= sizeof(src);
727 	assert(ctx->remaining >= 0);
728 	if (ctx->remaining < 0) {
729 		return -EOVERFLOW;
730 	}
731 
732 	memcpy(ctx->cursor, &val, sizeof(val));
733 	ctx->cursor += sizeof(src);
734 
735 	return 0;
736 }
737 
738 LIBPLDM_CC_NONNULL
739 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
740 						      const uint8_t src)
741 {
742 	if (!ctx->cursor) {
743 		return -EINVAL;
744 	}
745 
746 	static_assert(
747 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
748 		sizeof(src) < INTMAX_MAX,
749 		"The following addition may not uphold the runtime assertion");
750 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
751 		assert(ctx->remaining < 0);
752 		return -EOVERFLOW;
753 	}
754 	ctx->remaining -= sizeof(src);
755 	assert(ctx->remaining >= 0);
756 	if (ctx->remaining < 0) {
757 		return -EOVERFLOW;
758 	}
759 
760 	memcpy(ctx->cursor, &src, sizeof(src));
761 	ctx->cursor += sizeof(src);
762 
763 	return 0;
764 }
765 
766 LIBPLDM_CC_NONNULL
767 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
768 						      const int32_t src)
769 {
770 	int32_t val = htole32(src);
771 
772 	if (!ctx->cursor) {
773 		return -EINVAL;
774 	}
775 
776 	static_assert(
777 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
778 		sizeof(src) < INTMAX_MAX,
779 		"The following addition may not uphold the runtime assertion");
780 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
781 		assert(ctx->remaining < 0);
782 		return -EOVERFLOW;
783 	}
784 	ctx->remaining -= sizeof(src);
785 	assert(ctx->remaining >= 0);
786 	if (ctx->remaining < 0) {
787 		return -EOVERFLOW;
788 	}
789 
790 	memcpy(ctx->cursor, &val, sizeof(val));
791 	ctx->cursor += sizeof(src);
792 
793 	return 0;
794 }
795 
796 LIBPLDM_CC_NONNULL
797 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
798 						      const int16_t src)
799 {
800 	int16_t val = htole16(src);
801 
802 	if (!ctx->cursor) {
803 		return -EINVAL;
804 	}
805 
806 	static_assert(
807 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
808 		sizeof(src) < INTMAX_MAX,
809 		"The following addition may not uphold the runtime assertion");
810 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
811 		assert(ctx->remaining < 0);
812 		return -EOVERFLOW;
813 	}
814 	ctx->remaining -= sizeof(src);
815 	assert(ctx->remaining >= 0);
816 	if (ctx->remaining < 0) {
817 		return -EOVERFLOW;
818 	}
819 
820 	memcpy(ctx->cursor, &val, sizeof(val));
821 	ctx->cursor += sizeof(src);
822 
823 	return 0;
824 }
825 
826 LIBPLDM_CC_NONNULL
827 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
828 						     const int8_t src)
829 {
830 	if (!ctx->cursor) {
831 		return -EINVAL;
832 	}
833 
834 	static_assert(
835 		// NOLINTNEXTLINE(bugprone-sizeof-expression)
836 		sizeof(src) < INTMAX_MAX,
837 		"The following addition may not uphold the runtime assertion");
838 	if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
839 		assert(ctx->remaining < 0);
840 		return -EOVERFLOW;
841 	}
842 	ctx->remaining -= sizeof(src);
843 	assert(ctx->remaining >= 0);
844 	if (ctx->remaining < 0) {
845 		return -EOVERFLOW;
846 	}
847 
848 	memcpy(ctx->cursor, &src, sizeof(src));
849 	ctx->cursor += sizeof(src);
850 
851 	return 0;
852 }
853 
854 #define pldm_msgbuf_insert(dst, src)                                           \
855 	_Generic((src),                                                        \
856 		uint8_t: pldm_msgbuf_insert_uint8,                             \
857 		int8_t: pldm_msgbuf_insert_int8,                               \
858 		uint16_t: pldm_msgbuf_insert_uint16,                           \
859 		int16_t: pldm_msgbuf_insert_int16,                             \
860 		uint32_t: pldm_msgbuf_insert_uint32,                           \
861 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
862 
863 /**
864  * @ref pldm_msgbuf_insert_array
865  */
866 LIBPLDM_CC_NONNULL
867 LIBPLDM_CC_WARN_UNUSED_RESULT
868 LIBPLDM_CC_ALWAYS_INLINE int
869 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
870 pldm__msgbuf_insert_array_void(struct pldm_msgbuf *ctx, size_t count,
871 			       const void *src, size_t src_count)
872 {
873 	if (!ctx->cursor || count > src_count) {
874 		return -EINVAL;
875 	}
876 
877 	if (!count) {
878 		return 0;
879 	}
880 
881 #if INTMAX_MAX < SIZE_MAX
882 	if (count > INTMAX_MAX) {
883 		return -EOVERFLOW;
884 	}
885 #endif
886 
887 	if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
888 		return -EOVERFLOW;
889 	}
890 	ctx->remaining -= (intmax_t)count;
891 	assert(ctx->remaining >= 0);
892 	if (ctx->remaining < 0) {
893 		return -EOVERFLOW;
894 	}
895 
896 	memcpy(ctx->cursor, src, count);
897 	ctx->cursor += count;
898 
899 	return 0;
900 }
901 
902 /**
903  * @ref pldm_msgbuf_insert_array
904  */
905 LIBPLDM_CC_NONNULL
906 LIBPLDM_CC_WARN_UNUSED_RESULT
907 LIBPLDM_CC_ALWAYS_INLINE int
908 pldm_msgbuf_insert_array_char(struct pldm_msgbuf *ctx, size_t count,
909 			      const char *src, size_t src_count)
910 {
911 	return pldm__msgbuf_insert_array_void(ctx, count, src, src_count);
912 }
913 
914 /**
915  * @ref pldm_msgbuf_insert_array
916  */
917 LIBPLDM_CC_NONNULL
918 LIBPLDM_CC_WARN_UNUSED_RESULT
919 LIBPLDM_CC_ALWAYS_INLINE int
920 pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, size_t count,
921 			       const uint8_t *src, size_t src_count)
922 {
923 	return pldm__msgbuf_insert_array_void(ctx, count, src, src_count);
924 }
925 
926 /**
927  * Insert an array of data into the msgbuf instance
928  *
929  * @param ctx - The msgbuf instance into which the array of data should be
930  *              inserted
931  * @param count - The number of array elements to insert
932  * @param src - The array object from which elements should be inserted into
933                 @p ctx
934  * @param src_count - The maximum number of elements to insert from @p src
935  *
936  * Note that both @p count and @p src_count can only be counted by `sizeof` for
937  * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number
938  * of array elements and _not_ the object size of the array.
939  */
940 #define pldm_msgbuf_insert_array(dst, count, src, src_count)                   \
941 	_Generic((*(src)),                                                     \
942 		uint8_t: pldm_msgbuf_insert_array_uint8,                       \
943 		char: pldm_msgbuf_insert_array_char)(dst, count, src,          \
944 						     src_count)
945 
946 LIBPLDM_CC_NONNULL_ARGS(1)
947 LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
948 						       size_t required,
949 						       void **cursor)
950 {
951 	if (!ctx->cursor || (cursor && *cursor)) {
952 		return -EINVAL;
953 	}
954 
955 #if INTMAX_MAX < SIZE_MAX
956 	if (required > INTMAX_MAX) {
957 		return -EOVERFLOW;
958 	}
959 #endif
960 
961 	if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
962 		return -EOVERFLOW;
963 	}
964 	ctx->remaining -= (intmax_t)required;
965 	assert(ctx->remaining >= 0);
966 	if (ctx->remaining < 0) {
967 		return -EOVERFLOW;
968 	}
969 
970 	if (cursor) {
971 		*cursor = ctx->cursor;
972 	}
973 	ctx->cursor += required;
974 
975 	return 0;
976 }
977 
978 LIBPLDM_CC_NONNULL_ARGS(1)
979 LIBPLDM_CC_ALWAYS_INLINE int
980 pldm_msgbuf_span_string_ascii(struct pldm_msgbuf *ctx, void **cursor,
981 			      size_t *length)
982 {
983 	intmax_t measured;
984 
985 	if (!ctx->cursor || (cursor && *cursor)) {
986 		return -EINVAL;
987 	}
988 
989 	if (ctx->remaining < 0) {
990 		/* Tracking the amount of overflow gets disturbed here */
991 		return -EOVERFLOW;
992 	}
993 
994 	measured = (intmax_t)strnlen((const char *)ctx->cursor, ctx->remaining);
995 	if (measured == ctx->remaining) {
996 		/*
997 		 * We have hit the end of the buffer prior to the NUL terminator.
998 		 * Optimistically, the NUL terminator was one-beyond-the-end. Setting
999 		 * ctx->remaining negative ensures the `pldm_msgbuf_destroy*()` APIs also
1000 		 * return an error.
1001 		 */
1002 		ctx->remaining = -1;
1003 		return -EOVERFLOW;
1004 	}
1005 
1006 	/* Include the NUL terminator in the span length, as spans are opaque */
1007 	measured++;
1008 
1009 	if (ctx->remaining < INTMAX_MIN + measured) {
1010 		return -EOVERFLOW;
1011 	}
1012 
1013 	ctx->remaining -= measured;
1014 	assert(ctx->remaining >= 0);
1015 	if (ctx->remaining < 0) {
1016 		return -EOVERFLOW;
1017 	}
1018 
1019 	if (cursor) {
1020 		*cursor = ctx->cursor;
1021 	}
1022 
1023 	ctx->cursor += measured;
1024 
1025 	if (length) {
1026 		*length = measured;
1027 	}
1028 
1029 	return 0;
1030 }
1031 
1032 LIBPLDM_CC_NONNULL_ARGS(1)
1033 LIBPLDM_CC_ALWAYS_INLINE int
1034 pldm_msgbuf_span_string_utf16(struct pldm_msgbuf *ctx, void **cursor,
1035 			      size_t *length)
1036 {
1037 	static const char16_t term = 0;
1038 	ptrdiff_t measured;
1039 	void *end;
1040 
1041 	if (!ctx->cursor || (cursor && *cursor)) {
1042 		return -EINVAL;
1043 	}
1044 
1045 	if (ctx->remaining < 0) {
1046 		/* Tracking the amount of overflow gets disturbed here */
1047 		return -EOVERFLOW;
1048 	}
1049 
1050 	/*
1051 	 * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do
1052 	 * not form a UTF16 NUL _code-point_ due to alignment with respect to the
1053 	 * start of the string
1054 	 */
1055 	end = ctx->cursor;
1056 	do {
1057 		if (end != ctx->cursor) {
1058 			/*
1059 			 * If we've looped we've found a relatively-unaligned NUL code-point.
1060 			 * Scan again from a relatively-aligned start point.
1061 			 */
1062 			end = (char *)end + 1;
1063 		}
1064 		measured = (char *)end - (char *)ctx->cursor;
1065 		end = memmem(end, ctx->remaining - measured, &term,
1066 			     sizeof(term));
1067 	} while (end && ((uintptr_t)end & 1) != ((uintptr_t)ctx->cursor & 1));
1068 
1069 	if (!end) {
1070 		/*
1071 		 * Optimistically, the last required pattern byte was one beyond the end of
1072 		 * the buffer. Setting ctx->remaining negative ensures the
1073 		 * `pldm_msgbuf_destroy*()` APIs also return an error.
1074 		 */
1075 		ctx->remaining = -1;
1076 		return -EOVERFLOW;
1077 	}
1078 
1079 	end = (char *)end + sizeof(char16_t);
1080 	measured = (char *)end - (char *)ctx->cursor;
1081 
1082 #if INTMAX_MAX < PTRDIFF_MAX
1083 	if (measured >= INTMAX_MAX) {
1084 		return pldm_msgbuf_status(ctx, EOVERFLOW);
1085 	}
1086 #endif
1087 
1088 	if (ctx->remaining < INTMAX_MIN + (intmax_t)measured) {
1089 		assert(ctx->remaining < 0);
1090 		return -EOVERFLOW;
1091 	}
1092 
1093 	ctx->remaining -= (intmax_t)measured;
1094 	assert(ctx->remaining >= 0);
1095 	if (ctx->remaining < 0) {
1096 		return -EOVERFLOW;
1097 	}
1098 
1099 	if (cursor) {
1100 		*cursor = ctx->cursor;
1101 	}
1102 
1103 	ctx->cursor += measured;
1104 
1105 	if (length) {
1106 		*length = (size_t)measured;
1107 	}
1108 
1109 	return 0;
1110 }
1111 
1112 LIBPLDM_CC_NONNULL
1113 LIBPLDM_CC_ALWAYS_INLINE int
1114 pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
1115 {
1116 	if (!ctx->cursor || *cursor) {
1117 		return -EINVAL;
1118 	}
1119 
1120 	assert(ctx->remaining >= 0);
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  * @brief pldm_msgbuf copy data between two msg buffers
1135  *
1136  * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1137  * @param[in,out] dst - destination of copy from source
1138  * @param[in] size - size of data to be copied
1139  * @param[in] description - description of data copied
1140  *
1141  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1142  * PLDM_ERROR_INVALID_LENGTH otherwise.
1143  * PLDM_ERROR_INVALID_DATA if input is invalid
1144  */
1145 #define pldm_msgbuf_copy(dst, src, type, name)                                 \
1146 	pldm__msgbuf_copy(dst, src, sizeof(type), #name)
1147 LIBPLDM_CC_NONNULL
1148 LIBPLDM_CC_ALWAYS_INLINE int
1149 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
1150 pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1151 		  const char *description LIBPLDM_CC_UNUSED)
1152 {
1153 	if (!src->cursor || !dst->cursor) {
1154 		return -EINVAL;
1155 	}
1156 
1157 #if INTMAX_MAX < SIZE_MAX
1158 	if (size > INTMAX_MAX) {
1159 		return -EOVERFLOW;
1160 	}
1161 #endif
1162 
1163 	if (src->remaining < INTMAX_MIN + (intmax_t)size) {
1164 		return -EOVERFLOW;
1165 	}
1166 
1167 	if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
1168 		return -EOVERFLOW;
1169 	}
1170 
1171 	src->remaining -= (intmax_t)size;
1172 	assert(src->remaining >= 0);
1173 	if (src->remaining < 0) {
1174 		return -EOVERFLOW;
1175 	}
1176 
1177 	dst->remaining -= (intmax_t)size;
1178 	assert(dst->remaining >= 0);
1179 	if (dst->remaining < 0) {
1180 		return -EOVERFLOW;
1181 	}
1182 
1183 	memcpy(dst->cursor, src->cursor, size);
1184 	src->cursor += size;
1185 	dst->cursor += size;
1186 
1187 	return 0;
1188 }
1189 
1190 LIBPLDM_CC_NONNULL
1191 LIBPLDM_CC_WARN_UNUSED_RESULT
1192 LIBPLDM_CC_ALWAYS_INLINE int
1193 pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1194 {
1195 	void *ascii = NULL;
1196 	size_t len = 0;
1197 	int rc;
1198 
1199 	rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len);
1200 	if (rc < 0) {
1201 		return rc;
1202 	}
1203 
1204 	return pldm__msgbuf_insert_array_void(dst, len, ascii, len);
1205 }
1206 
1207 LIBPLDM_CC_NONNULL
1208 LIBPLDM_CC_WARN_UNUSED_RESULT
1209 LIBPLDM_CC_ALWAYS_INLINE int
1210 pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1211 {
1212 	void *utf16 = NULL;
1213 	size_t len = 0;
1214 	int rc;
1215 
1216 	rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len);
1217 	if (rc < 0) {
1218 		return rc;
1219 	}
1220 
1221 	return pldm__msgbuf_insert_array_void(dst, len, utf16, len);
1222 }
1223 
1224 #ifdef __cplusplus
1225 }
1226 #endif
1227 
1228 #ifdef __cplusplus
1229 #include <type_traits>
1230 
1231 template <typename T>
1232 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
1233 						void *buf)
1234 {
1235 	static_assert(std::is_same<uint8_t, T>::value);
1236 	return pldm__msgbuf_extract_uint8(ctx, buf);
1237 }
1238 
1239 template <typename T>
1240 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
1241 					       void *buf)
1242 {
1243 	static_assert(std::is_same<int8_t, T>::value);
1244 	return pldm__msgbuf_extract_int8(ctx, buf);
1245 }
1246 
1247 template <typename T>
1248 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
1249 						 void *buf)
1250 {
1251 	static_assert(std::is_same<uint16_t, T>::value);
1252 	return pldm__msgbuf_extract_uint16(ctx, buf);
1253 }
1254 
1255 template <typename T>
1256 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
1257 						void *buf)
1258 {
1259 	static_assert(std::is_same<int16_t, T>::value);
1260 	return pldm__msgbuf_extract_int16(ctx, buf);
1261 }
1262 
1263 template <typename T>
1264 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
1265 						 void *buf)
1266 {
1267 	static_assert(std::is_same<uint32_t, T>::value);
1268 	return pldm__msgbuf_extract_uint32(ctx, buf);
1269 }
1270 
1271 template <typename T>
1272 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
1273 						void *buf)
1274 {
1275 	static_assert(std::is_same<int32_t, T>::value);
1276 	return pldm__msgbuf_extract_int32(ctx, buf);
1277 }
1278 
1279 template <typename T>
1280 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
1281 						 void *buf)
1282 {
1283 	static_assert(std::is_same<real32_t, T>::value);
1284 	return pldm__msgbuf_extract_real32(ctx, buf);
1285 }
1286 #endif
1287 
1288 #endif /* BUF_H */
1289