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