xref: /openbmc/libpldm/src/msgbuf.h (revision 07febdbb)
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 <limits.h>
50 #include <stdbool.h>
51 #include <stdint.h>
52 #include <string.h>
53 #include <sys/types.h>
54 
55 struct pldm_msgbuf {
56 	uint8_t *cursor;
57 	ssize_t remaining;
58 };
59 
60 /**
61  * @brief Initialize pldm buf struct for buf extractor
62  *
63  * @param[out] ctx - pldm_msgbuf context for extractor
64  * @param[in] minsize - The minimum required length of buffer `buf`
65  * @param[in] buf - buffer to be extracted
66  * @param[in] len - size of buffer
67  *
68  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
69  * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
70  * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
71  */
72 static inline int pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize,
73 				   const void *buf, size_t len)
74 {
75 	if (!ctx || !buf) {
76 		return PLDM_ERROR_INVALID_DATA;
77 	}
78 
79 	if ((minsize > len) || (len > SSIZE_MAX)) {
80 		return PLDM_ERROR_INVALID_LENGTH;
81 	}
82 
83 	if ((uintptr_t)buf + len < len) {
84 		return PLDM_ERROR_INVALID_LENGTH;
85 	}
86 
87 	ctx->cursor = (uint8_t *)buf;
88 	ctx->remaining = (ssize_t)len;
89 
90 	return PLDM_SUCCESS;
91 }
92 
93 /**
94  * @brief Validate buffer overflow state
95  *
96  * @param[in] ctx - pldm_msgbuf context for extractor
97  *
98  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
99  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
100  * prior accesses would have occurred beyond the bounds of the buffer, and
101  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
102  * pointer.
103  */
104 static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
105 {
106 	if (!ctx) {
107 		return PLDM_ERROR_INVALID_DATA;
108 	}
109 
110 	return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
111 }
112 
113 /**
114  * @brief Test whether a message buffer has been exactly consumed
115  *
116  * @param[in] ctx - pldm_msgbuf context for extractor
117  *
118  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
119  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
120  * indicates that an incorrect sequence of accesses have occurred, and
121  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
122  * pointer.
123  */
124 static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
125 {
126 	if (!ctx) {
127 		return PLDM_ERROR_INVALID_DATA;
128 	}
129 
130 	return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
131 }
132 
133 /**
134  * @brief Destroy the pldm buf
135  *
136  * @param[in] ctx - pldm_msgbuf context for extractor
137  *
138  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
139  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
140  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
141  * bounds of the buffer.
142  */
143 static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
144 {
145 	int valid;
146 
147 	if (!ctx) {
148 		return PLDM_ERROR_INVALID_DATA;
149 	}
150 
151 	valid = pldm_msgbuf_validate(ctx);
152 
153 	ctx->cursor = NULL;
154 	ctx->remaining = 0;
155 
156 	return valid;
157 }
158 
159 /**
160  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
161  * has been completely consumed without overflow
162  *
163  * @param[in] ctx - pldm_msgbuf context
164  *
165  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
166  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
167  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
168  * have occurred byond the bounds of the buffer
169  */
170 static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
171 {
172 	int consumed;
173 
174 	if (!ctx) {
175 		return PLDM_ERROR_INVALID_DATA;
176 	}
177 
178 	consumed = pldm_msgbuf_consumed(ctx);
179 
180 	ctx->cursor = NULL;
181 	ctx->remaining = 0;
182 
183 	return consumed;
184 }
185 
186 /*
187  * Exploit the pre-processor to perform type checking by macro substitution.
188  *
189  * A C type is defined by its alignment as well as its object
190  * size, and compilers have a hammer to enforce it in the form of
191  * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
192  * the libpldm public API this presents a problem: Naively attempting to use the
193  * msgbuf APIs on a member of a packed struct would yield an error.
194  *
195  * The msgbuf APIs are implemented such that data is moved through unaligned
196  * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
197  * make the object pointers take a trip through `void *` at its API boundary.
198  * That presents a bit too much of an opportunity to non-surgically remove your
199  * own foot, so here we set about doing something to mitigate that as well.
200  *
201  * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
202  * only for the purpose of object sizes, disregarding alignment. We have a few
203  * constraints that cause some headaches:
204  *
205  * 1. We have to perform the type-check before a call through a C function,
206  *    as the function must take the object pointer argument as `void *`.
207  *    Essentially, this constrains us to doing something with macros.
208  *
209  * 2. While libpldm is a C library, its test suite is written in C++ to take
210  *    advantage of gtest.
211  *
212  * 3. Ideally we'd do something with C's `static_assert()`, however
213  *    `static_assert()` is defined as void, and as we're constrained to macros,
214  *    using `static_assert()` would require a statement-expression
215  *
216  * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
217  *    are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
218  *    the purpose of enabling statement-expressions in this one instance.
219  *
220  * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
221  *    however it's implemented in terms of `_Generic()`, which is not available
222  *    in C++.
223  *
224  * Combined this means we need separate solutions for C and C++.
225  *
226  * For C, as we don't have statement-expressions, we need to exploit some other
227  * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
228  * API function call. We also have to take care of the fact that the call-sites
229  * may be in the context of a variable assignment for error-handling purposes.
230  * The key observation is that we can use the comma operator as a sequence point
231  * to order the type check before the API call, discarding the "result" value of
232  * the type check and yielding the return value of the API call.
233  *
234  * C++ could be less of a headache than the C as we can leverage template
235  * functions. An advantage of template functions is that while their definition
236  * is driven by instantion, the definition does not appear at the source
237  * location of the instantation, which gives it a great leg-up over the problems
238  * we have in the C path. However, the use of the msgbuf APIs in the test suite
239  * still makes things somewhat tricky, as the call-sites in the test suite are
240  * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
241  * takes both the object type and the required type as template arguments, and
242  * then define the object pointer parameter as `void *` for a call through to
243  * the appropriate msgbuf API. However, because the msgbuf API call-sites are
244  * encapsulated in gtest macros, use of commas in the template specification
245  * causes pre-processor confusion. In this way we're constrained to only one
246  * template argument per function.
247  *
248  * Implement the C++ path using template functions that take the destination
249  * object type as a template argument, while the name of the function symbols
250  * are derived from the required type. The manual implementations of these
251  * appear at the end of the header. The type safety is actually enforced
252  * by `static_assert()` this time, as we can use statements as we're not
253  * constrained to an expression in the templated function body.
254  *
255  * The invocations of pldm_msgbuf_extract_typecheck() typically result in
256  * double-evaluation of some arguments. We're not yet bothered by this for two
257  * reasons:
258  *
259  * 1. The nature of the current call-sites are such that there are no
260  *    argument expressions that result in undesirable side-effects
261  *
262  * 2. It's an API internal to the libpldm implementation, and we can fix things
263  *    whenever something crops up the violates the observation in 1.
264  */
265 #ifdef __cplusplus
266 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
267 	pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
268 #else
269 #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
270 	(pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
271 #endif
272 
273 /**
274  * @brief pldm_msgbuf extractor for a uint8_t
275  *
276  * @param[inout] ctx - pldm_msgbuf context for extractor
277  * @param[out] dst - destination of extracted value
278  *
279  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
280  * PLDM_ERROR_INVALID_LENGTH otherwise.
281  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
282  */
283 #define pldm_msgbuf_extract_uint8(ctx, dst)                                    \
284 	pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8,     \
285 				      dst, ctx, dst)
286 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
287 static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
288 {
289 	if (!ctx || !ctx->cursor || !dst) {
290 		return PLDM_ERROR_INVALID_DATA;
291 	}
292 
293 	ctx->remaining -= sizeof(uint8_t);
294 	assert(ctx->remaining >= 0);
295 	if (ctx->remaining < 0) {
296 		return PLDM_ERROR_INVALID_LENGTH;
297 	}
298 
299 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
300 
301 	ctx->cursor++;
302 	return PLDM_SUCCESS;
303 }
304 
305 #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
306 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
307 				      ctx, dst)
308 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
309 static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
310 {
311 	if (!ctx || !ctx->cursor || !dst) {
312 		return PLDM_ERROR_INVALID_DATA;
313 	}
314 
315 	ctx->remaining -= sizeof(int8_t);
316 	assert(ctx->remaining >= 0);
317 	if (ctx->remaining < 0) {
318 		return PLDM_ERROR_INVALID_LENGTH;
319 	}
320 
321 	memcpy(dst, ctx->cursor, sizeof(int8_t));
322 	ctx->cursor++;
323 	return PLDM_SUCCESS;
324 }
325 
326 #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
327 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
328 				      dst, ctx, dst)
329 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
330 static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
331 					      void *dst)
332 {
333 	uint16_t ldst;
334 
335 	if (!ctx || !ctx->cursor || !dst) {
336 		return PLDM_ERROR_INVALID_DATA;
337 	}
338 
339 	// Check for buffer overflow. If we overflow, account for the request as
340 	// negative values in ctx->remaining. This way we can debug how far
341 	// we've overflowed.
342 	ctx->remaining -= sizeof(ldst);
343 
344 	// Prevent the access if it would overflow. First, assert so we blow up
345 	// the test suite right at the point of failure. However, cater to
346 	// -DNDEBUG by explicitly testing that the access is valid.
347 	assert(ctx->remaining >= 0);
348 	if (ctx->remaining < 0) {
349 		return PLDM_ERROR_INVALID_LENGTH;
350 	}
351 
352 	// Use memcpy() to have the compiler deal with any alignment
353 	// issues on the target architecture
354 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
355 
356 	// Only assign the target value once it's correctly decoded
357 	ldst = le16toh(ldst);
358 
359 	// Allow storing to unaligned
360 	memcpy(dst, &ldst, sizeof(ldst));
361 
362 	ctx->cursor += sizeof(ldst);
363 
364 	return PLDM_SUCCESS;
365 }
366 
367 #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
368 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
369 				      dst, ctx, dst)
370 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
371 static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
372 {
373 	int16_t ldst;
374 
375 	if (!ctx || !ctx->cursor || !dst) {
376 		return PLDM_ERROR_INVALID_DATA;
377 	}
378 
379 	ctx->remaining -= sizeof(ldst);
380 	assert(ctx->remaining >= 0);
381 	if (ctx->remaining < 0) {
382 		return PLDM_ERROR_INVALID_LENGTH;
383 	}
384 
385 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
386 
387 	ldst = le16toh(ldst);
388 	memcpy(dst, &ldst, sizeof(ldst));
389 	ctx->cursor += sizeof(ldst);
390 
391 	return PLDM_SUCCESS;
392 }
393 
394 #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
395 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
396 				      dst, ctx, dst)
397 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
398 static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
399 					      void *dst)
400 {
401 	uint32_t ldst;
402 
403 	if (!ctx || !ctx->cursor || !dst) {
404 		return PLDM_ERROR_INVALID_DATA;
405 	}
406 
407 	ctx->remaining -= sizeof(ldst);
408 	assert(ctx->remaining >= 0);
409 	if (ctx->remaining < 0) {
410 		return PLDM_ERROR_INVALID_LENGTH;
411 	}
412 
413 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
414 
415 	ldst = le32toh(ldst);
416 	memcpy(dst, &ldst, sizeof(ldst));
417 	ctx->cursor += sizeof(ldst);
418 
419 	return PLDM_SUCCESS;
420 }
421 
422 #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
423 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
424 				      dst, ctx, dst)
425 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
426 static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
427 {
428 	int32_t ldst;
429 
430 	if (!ctx || !ctx->cursor || !dst) {
431 		return PLDM_ERROR_INVALID_DATA;
432 	}
433 
434 	ctx->remaining -= sizeof(ldst);
435 	assert(ctx->remaining >= 0);
436 	if (ctx->remaining < 0) {
437 		return PLDM_ERROR_INVALID_LENGTH;
438 	}
439 
440 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
441 
442 	ldst = le32toh(ldst);
443 	memcpy(dst, &ldst, sizeof(ldst));
444 	ctx->cursor += sizeof(ldst);
445 
446 	return PLDM_SUCCESS;
447 }
448 
449 #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
450 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
451 				      dst, ctx, dst)
452 // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
453 static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx,
454 					      void *dst)
455 {
456 	uint32_t ldst;
457 
458 	_Static_assert(sizeof(real32_t) == sizeof(ldst),
459 		       "Mismatched type sizes for dst and ldst");
460 
461 	if (!ctx || !ctx->cursor || !dst) {
462 		return PLDM_ERROR_INVALID_DATA;
463 	}
464 
465 	ctx->remaining -= sizeof(ldst);
466 	assert(ctx->remaining >= 0);
467 	if (ctx->remaining < 0) {
468 		return PLDM_ERROR_INVALID_LENGTH;
469 	}
470 
471 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
472 	ldst = le32toh(ldst);
473 	memcpy(dst, &ldst, sizeof(ldst));
474 	ctx->cursor += sizeof(ldst);
475 
476 	return PLDM_SUCCESS;
477 }
478 
479 /**
480  * Extract the field at the msgbuf cursor into the lvalue named by dst.
481  *
482  * @param ctx The msgbuf context object
483  * @param dst The lvalue into which the field at the msgbuf cursor should be
484  *            extracted
485  *
486  * @return PLDM_SUCCESS on success, otherwise another value on error
487  */
488 #define pldm_msgbuf_extract(ctx, dst)                                          \
489 	_Generic((dst),                                                        \
490 		uint8_t: pldm__msgbuf_extract_uint8,                           \
491 		int8_t: pldm__msgbuf_extract_int8,                             \
492 		uint16_t: pldm__msgbuf_extract_uint16,                         \
493 		int16_t: pldm__msgbuf_extract_int16,                           \
494 		uint32_t: pldm__msgbuf_extract_uint32,                         \
495 		int32_t: pldm__msgbuf_extract_int32,                           \
496 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
497 
498 /**
499  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
500  *
501  * @param ctx The msgbuf context object
502  * @param dst The pointer to the object into which the field at the msgbuf
503  *            cursor should be extracted
504  *
505  * @return PLDM_SUCCESS on success, otherwise another value on error
506  */
507 #define pldm_msgbuf_extract_p(ctx, dst)                                        \
508 	_Generic((dst),                                                        \
509 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
510 		int8_t *: pldm__msgbuf_extract_int8,                           \
511 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
512 		int16_t *: pldm__msgbuf_extract_int16,                         \
513 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
514 		int32_t *: pldm__msgbuf_extract_int32,                         \
515 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
516 
517 static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
518 						  uint8_t *dst, size_t count)
519 {
520 	if (!ctx || !ctx->cursor || !dst) {
521 		return PLDM_ERROR_INVALID_DATA;
522 	}
523 
524 	if (!count) {
525 		return PLDM_SUCCESS;
526 	}
527 
528 	if (count >= SSIZE_MAX) {
529 		return PLDM_ERROR_INVALID_LENGTH;
530 	}
531 
532 	ctx->remaining -= (ssize_t)count;
533 	assert(ctx->remaining >= 0);
534 	if (ctx->remaining < 0) {
535 		return PLDM_ERROR_INVALID_LENGTH;
536 	}
537 
538 	memcpy(dst, ctx->cursor, count);
539 	ctx->cursor += count;
540 
541 	return PLDM_SUCCESS;
542 }
543 
544 #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
545 	_Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
546 								     count)
547 
548 static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
549 					    const uint32_t src)
550 {
551 	uint32_t val = htole32(src);
552 
553 	if (!ctx || !ctx->cursor) {
554 		return PLDM_ERROR_INVALID_DATA;
555 	}
556 
557 	ctx->remaining -= sizeof(src);
558 	assert(ctx->remaining >= 0);
559 	if (ctx->remaining < 0) {
560 		return PLDM_ERROR_INVALID_LENGTH;
561 	}
562 
563 	memcpy(ctx->cursor, &val, sizeof(val));
564 	ctx->cursor += sizeof(src);
565 
566 	return PLDM_SUCCESS;
567 }
568 
569 static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
570 					    const uint16_t src)
571 {
572 	uint16_t val = htole16(src);
573 
574 	if (!ctx || !ctx->cursor) {
575 		return PLDM_ERROR_INVALID_DATA;
576 	}
577 
578 	ctx->remaining -= sizeof(src);
579 	assert(ctx->remaining >= 0);
580 	if (ctx->remaining < 0) {
581 		return PLDM_ERROR_INVALID_LENGTH;
582 	}
583 
584 	memcpy(ctx->cursor, &val, sizeof(val));
585 	ctx->cursor += sizeof(src);
586 
587 	return PLDM_SUCCESS;
588 }
589 
590 static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
591 					   const uint8_t src)
592 {
593 	if (!ctx || !ctx->cursor) {
594 		return PLDM_ERROR_INVALID_DATA;
595 	}
596 
597 	ctx->remaining -= sizeof(src);
598 	assert(ctx->remaining >= 0);
599 	if (ctx->remaining < 0) {
600 		return PLDM_ERROR_INVALID_LENGTH;
601 	}
602 
603 	memcpy(ctx->cursor, &src, sizeof(src));
604 	ctx->cursor += sizeof(src);
605 
606 	return PLDM_SUCCESS;
607 }
608 
609 static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
610 					   const int32_t src)
611 {
612 	int32_t val = htole32(src);
613 
614 	if (!ctx || !ctx->cursor) {
615 		return PLDM_ERROR_INVALID_DATA;
616 	}
617 
618 	ctx->remaining -= sizeof(src);
619 	assert(ctx->remaining >= 0);
620 	if (ctx->remaining < 0) {
621 		return PLDM_ERROR_INVALID_LENGTH;
622 	}
623 
624 	memcpy(ctx->cursor, &val, sizeof(val));
625 	ctx->cursor += sizeof(src);
626 
627 	return PLDM_SUCCESS;
628 }
629 
630 static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
631 					   const int16_t src)
632 {
633 	int16_t val = htole16(src);
634 
635 	if (!ctx || !ctx->cursor) {
636 		return PLDM_ERROR_INVALID_DATA;
637 	}
638 
639 	ctx->remaining -= sizeof(src);
640 	assert(ctx->remaining >= 0);
641 	if (ctx->remaining < 0) {
642 		return PLDM_ERROR_INVALID_LENGTH;
643 	}
644 
645 	memcpy(ctx->cursor, &val, sizeof(val));
646 	ctx->cursor += sizeof(src);
647 
648 	return PLDM_SUCCESS;
649 }
650 
651 static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
652 					  const int8_t src)
653 {
654 	if (!ctx || !ctx->cursor) {
655 		return PLDM_ERROR_INVALID_DATA;
656 	}
657 
658 	ctx->remaining -= sizeof(src);
659 	assert(ctx->remaining >= 0);
660 	if (ctx->remaining < 0) {
661 		return PLDM_ERROR_INVALID_LENGTH;
662 	}
663 
664 	memcpy(ctx->cursor, &src, sizeof(src));
665 	ctx->cursor += sizeof(src);
666 
667 	return PLDM_SUCCESS;
668 }
669 
670 #define pldm_msgbuf_insert(dst, src)                                           \
671 	_Generic((src),                                                        \
672 		uint8_t: pldm_msgbuf_insert_uint8,                             \
673 		int8_t: pldm_msgbuf_insert_int8,                               \
674 		uint16_t: pldm_msgbuf_insert_uint16,                           \
675 		int16_t: pldm_msgbuf_insert_int16,                             \
676 		uint32_t: pldm_msgbuf_insert_uint32,                           \
677 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
678 
679 static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
680 						 const uint8_t *src,
681 						 size_t count)
682 {
683 	if (!ctx || !ctx->cursor || !src) {
684 		return PLDM_ERROR_INVALID_DATA;
685 	}
686 
687 	if (!count) {
688 		return PLDM_SUCCESS;
689 	}
690 
691 	if (count >= SSIZE_MAX) {
692 		return PLDM_ERROR_INVALID_LENGTH;
693 	}
694 
695 	ctx->remaining -= (ssize_t)count;
696 	assert(ctx->remaining >= 0);
697 	if (ctx->remaining < 0) {
698 		return PLDM_ERROR_INVALID_LENGTH;
699 	}
700 
701 	memcpy(ctx->cursor, src, count);
702 	ctx->cursor += count;
703 
704 	return PLDM_SUCCESS;
705 }
706 
707 #define pldm_msgbuf_insert_array(dst, src, count)                              \
708 	_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src,  \
709 								    count)
710 
711 static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
712 					    size_t required, void **cursor)
713 {
714 	if (!ctx || !ctx->cursor || !cursor || *cursor) {
715 		return PLDM_ERROR_INVALID_DATA;
716 	}
717 
718 	if (required > SSIZE_MAX) {
719 		return PLDM_ERROR_INVALID_LENGTH;
720 	}
721 
722 	ctx->remaining -= (ssize_t)required;
723 	assert(ctx->remaining >= 0);
724 	if (ctx->remaining < 0) {
725 		return PLDM_ERROR_INVALID_LENGTH;
726 	}
727 
728 	*cursor = ctx->cursor;
729 	ctx->cursor += required;
730 
731 	return PLDM_SUCCESS;
732 }
733 
734 static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
735 					     void **cursor, size_t *len)
736 {
737 	if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
738 		return PLDM_ERROR_INVALID_DATA;
739 	}
740 
741 	assert(ctx->remaining >= 0);
742 	if (ctx->remaining < 0) {
743 		return PLDM_ERROR_INVALID_LENGTH;
744 	}
745 
746 	*cursor = ctx->cursor;
747 	ctx->cursor += ctx->remaining;
748 	*len = ctx->remaining;
749 	ctx->remaining = 0;
750 
751 	return PLDM_SUCCESS;
752 }
753 #ifdef __cplusplus
754 }
755 #endif
756 
757 #ifdef __cplusplus
758 #include <type_traits>
759 
760 template <typename T>
761 static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
762 						void *buf)
763 {
764 	static_assert(std::is_same<uint8_t *, T>::value);
765 	return pldm__msgbuf_extract_uint8(ctx, buf);
766 }
767 
768 template <typename T>
769 static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
770 					       void *buf)
771 {
772 	static_assert(std::is_same<int8_t *, T>::value);
773 	return pldm__msgbuf_extract_int8(ctx, buf);
774 }
775 
776 template <typename T>
777 static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
778 						 void *buf)
779 {
780 	static_assert(std::is_same<uint16_t *, T>::value);
781 	return pldm__msgbuf_extract_uint16(ctx, buf);
782 }
783 
784 template <typename T>
785 static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
786 						void *buf)
787 {
788 	static_assert(std::is_same<int16_t *, T>::value);
789 	return pldm__msgbuf_extract_int16(ctx, buf);
790 }
791 
792 template <typename T>
793 static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
794 						 void *buf)
795 {
796 	static_assert(std::is_same<uint32_t *, T>::value);
797 	return pldm__msgbuf_extract_uint32(ctx, buf);
798 }
799 
800 template <typename T>
801 static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
802 						void *buf)
803 {
804 	static_assert(std::is_same<int32_t *, T>::value);
805 	return pldm__msgbuf_extract_int32(ctx, buf);
806 }
807 
808 template <typename T>
809 static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
810 						 void *buf)
811 {
812 	static_assert(std::is_same<real32_t *, T>::value);
813 	return pldm__msgbuf_extract_real32(ctx, buf);
814 }
815 #endif
816 
817 #endif /* BUF_H */
818