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