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