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