blob: 37ad470e900219148847f9de60491b52a6523886 [file] [log] [blame]
Patrick Williams691668f2023-11-01 08:19:10 -05001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
Andrew Jefferyc63f63a2023-02-24 22:29:33 +10302#ifndef PLDM_MSGBUF_H
3#define PLDM_MSGBUF_H
4
Andrew Jeffery66c77232024-04-24 11:42:02 +09305/*
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
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103031#ifdef __cplusplus
Andrew Jeffery37dd6a32023-05-12 16:04:06 +093032/*
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__)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103039extern "C" {
40#endif
41
Andrew Jefferyb0c1d202023-11-07 22:08:44 +103042#include <libpldm/base.h>
43#include <libpldm/pldm_types.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103044
Andrew Jeffery66c77232024-04-24 11:42:02 +093045#include "compiler.h"
46
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103047#include <assert.h>
48#include <endian.h>
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093049#include <errno.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103050#include <limits.h>
51#include <stdbool.h>
Andrew Jeffery66c77232024-04-24 11:42:02 +093052#include <stdint.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103053#include <string.h>
54#include <sys/types.h>
55
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093056/*
57 * We can't use static_assert() outside of some other C construct. Deal
58 * with high-level global assertions by burying them in an unused struct
59 * declaration, that has a sole member for compliance with the requirement that
60 * types must have a size.
61*/
62static struct {
63 static_assert(
64 INTMAX_MAX != SIZE_MAX,
65 "Extraction and insertion value comparisons may be broken");
66 static_assert(INTMAX_MIN + INTMAX_MAX <= 0,
67 "Extraction and insertion arithmetic may be broken");
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093068 static_assert(PLDM_SUCCESS == 0, "Error handling is broken");
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093069 int compliance;
70} build_assertions __attribute__((unused));
71
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093072enum pldm_msgbuf_error_mode {
73 PLDM_MSGBUF_PLDM_CC = 0x5a,
74 PLDM_MSGBUF_C_ERRNO = 0xa5,
75};
76
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103077struct pldm_msgbuf {
Thu Nguyen062c8762023-04-22 20:45:04 +070078 uint8_t *cursor;
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093079 intmax_t remaining;
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093080 enum pldm_msgbuf_error_mode mode;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103081};
82
Andrew Jefferyd861a682024-06-03 21:43:09 +093083/**
84 * @brief Either negate an errno value or return a value mapped to a PLDM
85 * completion code.
86 *
87 * Note that `pldm_msgbuf_status()` is purely internal to the msgbuf API
88 * for ergonomics. It's preferred that we don't try to unify this with
89 * `pldm_xlate_errno()` from src/api.h despite the similarities.
90 *
91 * @param[in] ctx - The msgbuf context providing the personality info
92 * @param[in] err - The positive errno value to translate
93 *
94 * @return Either the negated value of @p err if the context's error mode is
95 * `PLDM_MSGBUF_C_ERRNO`, or the equivalent PLDM completion code if the
96 * error mode is `PLDM_MSGBUF_PLDM_CC`.
97 */
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093098__attribute__((always_inline)) static inline int
99pldm_msgbuf_status(struct pldm_msgbuf *ctx, unsigned int err)
100{
101 int rc;
102
103 assert(err != 0);
104 assert(err <= INT_MAX);
105
106 if (ctx->mode == PLDM_MSGBUF_C_ERRNO) {
107 if (err > INT_MAX) {
108 return -EINVAL;
109 }
110
111 static_assert(INT_MIN + INT_MAX < 0,
112 "Arithmetic assumption failure");
113 return -((int)err);
114 }
115
116 if (err > INT_MAX) {
117 return PLDM_ERROR;
118 }
119
120 assert(ctx->mode == PLDM_MSGBUF_PLDM_CC);
121 switch (err) {
122 case EINVAL:
123 rc = PLDM_ERROR_INVALID_DATA;
124 break;
125 case EBADMSG:
126 case EOVERFLOW:
127 rc = PLDM_ERROR_INVALID_LENGTH;
128 break;
129 default:
130 assert(false);
131 rc = PLDM_ERROR;
132 break;
133 }
134
135 assert(rc > 0);
136 return rc;
137}
138
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030139/**
140 * @brief Initialize pldm buf struct for buf extractor
141 *
142 * @param[out] ctx - pldm_msgbuf context for extractor
143 * @param[in] minsize - The minimum required length of buffer `buf`
144 * @param[in] buf - buffer to be extracted
145 * @param[in] len - size of buffer
146 *
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930147 * @return 0 on success, otherwise an error code appropriate for the current
148 * personality.
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030149 */
Andrew Jeffery76712f62024-05-22 15:19:00 +0930150__attribute__((always_inline)) static inline int
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930151// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
152pldm__msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
153 size_t len)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030154{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930155 assert(ctx);
156 assert(ctx->mode == PLDM_MSGBUF_PLDM_CC ||
157 ctx->mode == PLDM_MSGBUF_C_ERRNO);
158
159 if (!buf) {
160 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030161 }
162
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930163 if ((minsize > len)) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930164 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030165 }
166
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930167#if INTMAX_MAX < SIZE_MAX
168 if (len > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930169 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930170 }
171#endif
172
Andrew Jeffery07febdb2024-05-17 14:17:14 +0930173 if ((uintptr_t)buf + len < len) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930174 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030175 }
176
177 ctx->cursor = (uint8_t *)buf;
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930178 ctx->remaining = (intmax_t)len;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030179
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930180 return 0;
181}
182
183/**
184 * @brief Initialise a msgbuf instance to return errors as PLDM completion codes
185 *
186 * @see pldm__msgbuf_init
187 *
188 * @param[out] ctx - pldm_msgbuf context for extractor
189 * @param[in] minsize - The minimum required length of buffer `buf`
190 * @param[in] buf - buffer to be extracted
191 * @param[in] len - size of buffer
192 *
193 * @return PLDM_SUCCESS if the provided buffer region is sensible,
194 * otherwise PLDM_ERROR_INVALID_DATA if pointer parameters are invalid,
195 * or PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
196 */
197__attribute__((always_inline)) static inline int
198pldm_msgbuf_init_cc(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
199 size_t len)
200{
201 if (!ctx) {
202 return PLDM_ERROR_INVALID_DATA;
203 }
204
205 ctx->mode = PLDM_MSGBUF_PLDM_CC;
206 return pldm__msgbuf_init(ctx, minsize, buf, len);
207}
208
209/**
210 * @brief Initialise a msgbuf instance to return errors as negative errno values
211 *
212 * @see pldm__msgbuf_init
213 *
214 * @param[out] ctx - pldm_msgbuf context for extractor
215 * @param[in] minsize - The minimum required length of buffer `buf`
216 * @param[in] buf - buffer to be extracted
217 * @param[in] len - size of buffer
218 *
219 * @return 0 if the provided buffer region is sensible, otherwise -EINVAL if
220 * pointer parameters are invalid, or -EOVERFLOW if length constraints
221 * are violated.
222 */
223__attribute__((always_inline)) static inline int
224pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
225 size_t len)
226{
227 if (!ctx) {
228 return -EINVAL;
229 }
230
231 ctx->mode = PLDM_MSGBUF_C_ERRNO;
232 return pldm__msgbuf_init(ctx, minsize, buf, len);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030233}
234
235/**
236 * @brief Validate buffer overflow state
237 *
238 * @param[in] ctx - pldm_msgbuf context for extractor
239 *
240 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
241 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
242 * prior accesses would have occurred beyond the bounds of the buffer, and
243 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
244 * pointer.
245 */
Andrew Jeffery76712f62024-05-22 15:19:00 +0930246__attribute__((always_inline)) static inline int
247pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030248{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930249 assert(ctx);
250 if (ctx->remaining < 0) {
251 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030252 }
253
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930254 return 0;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030255}
256
257/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930258 * @brief Test whether a message buffer has been exactly consumed
259 *
260 * @param[in] ctx - pldm_msgbuf context for extractor
261 *
262 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
263 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
264 * indicates that an incorrect sequence of accesses have occurred, and
265 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
266 * pointer.
267 */
Andrew Jeffery76712f62024-05-22 15:19:00 +0930268__attribute__((always_inline)) static inline int
269pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930270{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930271 assert(ctx);
272 if (ctx->remaining != 0) {
273 return pldm_msgbuf_status(ctx, EBADMSG);
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930274 }
275
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930276 return 0;
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930277}
278
279/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030280 * @brief Destroy the pldm buf
281 *
282 * @param[in] ctx - pldm_msgbuf context for extractor
283 *
284 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
285 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
286 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
287 * bounds of the buffer.
288 */
Andrew Jeffery76712f62024-05-22 15:19:00 +0930289__attribute__((always_inline)) static inline int
290pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030291{
292 int valid;
293
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930294 assert(ctx);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030295 valid = pldm_msgbuf_validate(ctx);
296
297 ctx->cursor = NULL;
298 ctx->remaining = 0;
299
300 return valid;
301}
302
303/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930304 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
305 * has been completely consumed without overflow
306 *
307 * @param[in] ctx - pldm_msgbuf context
308 *
309 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
310 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
311 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
312 * have occurred byond the bounds of the buffer
313 */
Andrew Jeffery76712f62024-05-22 15:19:00 +0930314__attribute__((always_inline)) static inline int
315pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930316{
317 int consumed;
318
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930319 assert(ctx);
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930320 consumed = pldm_msgbuf_consumed(ctx);
321
322 ctx->cursor = NULL;
323 ctx->remaining = 0;
324
325 return consumed;
326}
327
Andrew Jeffery66c77232024-04-24 11:42:02 +0930328/*
329 * Exploit the pre-processor to perform type checking by macro substitution.
330 *
331 * A C type is defined by its alignment as well as its object
332 * size, and compilers have a hammer to enforce it in the form of
333 * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
334 * the libpldm public API this presents a problem: Naively attempting to use the
335 * msgbuf APIs on a member of a packed struct would yield an error.
336 *
337 * The msgbuf APIs are implemented such that data is moved through unaligned
338 * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
339 * make the object pointers take a trip through `void *` at its API boundary.
340 * That presents a bit too much of an opportunity to non-surgically remove your
341 * own foot, so here we set about doing something to mitigate that as well.
342 *
343 * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
344 * only for the purpose of object sizes, disregarding alignment. We have a few
345 * constraints that cause some headaches:
346 *
347 * 1. We have to perform the type-check before a call through a C function,
348 * as the function must take the object pointer argument as `void *`.
349 * Essentially, this constrains us to doing something with macros.
350 *
351 * 2. While libpldm is a C library, its test suite is written in C++ to take
352 * advantage of gtest.
353 *
354 * 3. Ideally we'd do something with C's `static_assert()`, however
355 * `static_assert()` is defined as void, and as we're constrained to macros,
356 * using `static_assert()` would require a statement-expression
357 *
358 * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
359 * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
360 * the purpose of enabling statement-expressions in this one instance.
361 *
362 * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
363 * however it's implemented in terms of `_Generic()`, which is not available
364 * in C++.
365 *
366 * Combined this means we need separate solutions for C and C++.
367 *
368 * For C, as we don't have statement-expressions, we need to exploit some other
369 * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
370 * API function call. We also have to take care of the fact that the call-sites
371 * may be in the context of a variable assignment for error-handling purposes.
372 * The key observation is that we can use the comma operator as a sequence point
373 * to order the type check before the API call, discarding the "result" value of
374 * the type check and yielding the return value of the API call.
375 *
376 * C++ could be less of a headache than the C as we can leverage template
377 * functions. An advantage of template functions is that while their definition
378 * is driven by instantion, the definition does not appear at the source
Manojkiran Eda9e3a5d42024-06-17 16:06:42 +0530379 * location of the instantiation, which gives it a great leg-up over the problems
Andrew Jeffery66c77232024-04-24 11:42:02 +0930380 * we have in the C path. However, the use of the msgbuf APIs in the test suite
381 * still makes things somewhat tricky, as the call-sites in the test suite are
382 * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
383 * takes both the object type and the required type as template arguments, and
384 * then define the object pointer parameter as `void *` for a call through to
385 * the appropriate msgbuf API. However, because the msgbuf API call-sites are
386 * encapsulated in gtest macros, use of commas in the template specification
387 * causes pre-processor confusion. In this way we're constrained to only one
388 * template argument per function.
389 *
390 * Implement the C++ path using template functions that take the destination
391 * object type as a template argument, while the name of the function symbols
392 * are derived from the required type. The manual implementations of these
393 * appear at the end of the header. The type safety is actually enforced
394 * by `static_assert()` this time, as we can use statements as we're not
395 * constrained to an expression in the templated function body.
396 *
397 * The invocations of pldm_msgbuf_extract_typecheck() typically result in
398 * double-evaluation of some arguments. We're not yet bothered by this for two
399 * reasons:
400 *
401 * 1. The nature of the current call-sites are such that there are no
402 * argument expressions that result in undesirable side-effects
403 *
404 * 2. It's an API internal to the libpldm implementation, and we can fix things
405 * whenever something crops up the violates the observation in 1.
406 */
407#ifdef __cplusplus
408#define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
409 pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
410#else
411#define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
412 (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
413#endif
414
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930415/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030416 * @brief pldm_msgbuf extractor for a uint8_t
417 *
Manojkiran Eda9e3a5d42024-06-17 16:06:42 +0530418 * @param[in,out] ctx - pldm_msgbuf context for extractor
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030419 * @param[out] dst - destination of extracted value
420 *
421 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
422 * PLDM_ERROR_INVALID_LENGTH otherwise.
423 * PLDM_ERROR_INVALID_DATA if input a invalid ctx
424 */
Andrew Jeffery66c77232024-04-24 11:42:02 +0930425#define pldm_msgbuf_extract_uint8(ctx, dst) \
426 pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \
427 dst, ctx, dst)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930428__attribute__((always_inline)) static inline int
Andrew Jeffery66c77232024-04-24 11:42:02 +0930429// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930430pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030431{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930432 assert(ctx);
433
434 if (!ctx->cursor || !dst) {
435 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030436 }
437
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930438 if (ctx->remaining == INTMAX_MIN) {
439 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930440 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930441 }
Andrew Jeffery66c77232024-04-24 11:42:02 +0930442 ctx->remaining -= sizeof(uint8_t);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030443 assert(ctx->remaining >= 0);
444 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930445 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030446 }
447
Andrew Jeffery66c77232024-04-24 11:42:02 +0930448 memcpy(dst, ctx->cursor, sizeof(uint8_t));
449
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030450 ctx->cursor++;
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930451 return 0;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030452}
453
Andrew Jeffery66c77232024-04-24 11:42:02 +0930454#define pldm_msgbuf_extract_int8(ctx, dst) \
455 pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \
456 ctx, dst)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930457__attribute__((always_inline)) static inline int
Andrew Jeffery66c77232024-04-24 11:42:02 +0930458// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930459pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030460{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930461 assert(ctx);
462
463 if (!ctx->cursor || !dst) {
464 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030465 }
466
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930467 if (ctx->remaining == INTMAX_MIN) {
468 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930469 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930470 }
Andrew Jeffery66c77232024-04-24 11:42:02 +0930471 ctx->remaining -= sizeof(int8_t);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030472 assert(ctx->remaining >= 0);
473 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930474 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030475 }
476
Andrew Jeffery66c77232024-04-24 11:42:02 +0930477 memcpy(dst, ctx->cursor, sizeof(int8_t));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030478 ctx->cursor++;
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930479 return 0;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030480}
481
Andrew Jeffery66c77232024-04-24 11:42:02 +0930482#define pldm_msgbuf_extract_uint16(ctx, dst) \
483 pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \
484 dst, ctx, dst)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930485__attribute__((always_inline)) static inline int
Andrew Jeffery66c77232024-04-24 11:42:02 +0930486// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930487pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030488{
489 uint16_t ldst;
490
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930491 assert(ctx);
492
493 if (!ctx->cursor || !dst) {
494 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030495 }
496
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930497 // Check for underflow while tracking the magnitude of the buffer overflow
498 static_assert(
499 // NOLINTNEXTLINE(bugprone-sizeof-expression)
500 sizeof(ldst) < INTMAX_MAX,
501 "The following addition may not uphold the runtime assertion");
502 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
503 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930504 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930505 }
506
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030507 // Check for buffer overflow. If we overflow, account for the request as
508 // negative values in ctx->remaining. This way we can debug how far
509 // we've overflowed.
510 ctx->remaining -= sizeof(ldst);
511
512 // Prevent the access if it would overflow. First, assert so we blow up
513 // the test suite right at the point of failure. However, cater to
514 // -DNDEBUG by explicitly testing that the access is valid.
515 assert(ctx->remaining >= 0);
516 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930517 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030518 }
519
520 // Use memcpy() to have the compiler deal with any alignment
521 // issues on the target architecture
522 memcpy(&ldst, ctx->cursor, sizeof(ldst));
523
524 // Only assign the target value once it's correctly decoded
Andrew Jeffery66c77232024-04-24 11:42:02 +0930525 ldst = le16toh(ldst);
526
527 // Allow storing to unaligned
528 memcpy(dst, &ldst, sizeof(ldst));
529
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030530 ctx->cursor += sizeof(ldst);
531
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930532 return 0;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030533}
534
Andrew Jeffery66c77232024-04-24 11:42:02 +0930535#define pldm_msgbuf_extract_int16(ctx, dst) \
536 pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \
537 dst, ctx, dst)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930538__attribute__((always_inline)) static inline int
Andrew Jeffery66c77232024-04-24 11:42:02 +0930539// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930540pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030541{
542 int16_t ldst;
543
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930544 assert(ctx);
545
546 if (!ctx->cursor || !dst) {
547 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030548 }
549
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930550 static_assert(
551 // NOLINTNEXTLINE(bugprone-sizeof-expression)
552 sizeof(ldst) < INTMAX_MAX,
553 "The following addition may not uphold the runtime assertion");
554 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
555 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930556 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930557 }
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030558 ctx->remaining -= sizeof(ldst);
559 assert(ctx->remaining >= 0);
560 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930561 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030562 }
563
564 memcpy(&ldst, ctx->cursor, sizeof(ldst));
565
Andrew Jeffery66c77232024-04-24 11:42:02 +0930566 ldst = le16toh(ldst);
567 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030568 ctx->cursor += sizeof(ldst);
569
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930570 return 0;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030571}
572
Andrew Jeffery66c77232024-04-24 11:42:02 +0930573#define pldm_msgbuf_extract_uint32(ctx, dst) \
574 pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \
575 dst, ctx, dst)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930576__attribute__((always_inline)) static inline int
Andrew Jeffery66c77232024-04-24 11:42:02 +0930577// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930578pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030579{
580 uint32_t ldst;
581
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930582 assert(ctx);
583
584 if (!ctx->cursor || !dst) {
585 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030586 }
587
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930588 static_assert(
589 // NOLINTNEXTLINE(bugprone-sizeof-expression)
590 sizeof(ldst) < INTMAX_MAX,
591 "The following addition may not uphold the runtime assertion");
592 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
593 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930594 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930595 }
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030596 ctx->remaining -= sizeof(ldst);
597 assert(ctx->remaining >= 0);
598 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930599 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030600 }
601
602 memcpy(&ldst, ctx->cursor, sizeof(ldst));
Andrew Jeffery66c77232024-04-24 11:42:02 +0930603 ldst = le32toh(ldst);
604 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030605 ctx->cursor += sizeof(ldst);
606
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930607 return 0;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030608}
609
Andrew Jeffery66c77232024-04-24 11:42:02 +0930610#define pldm_msgbuf_extract_int32(ctx, dst) \
611 pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \
612 dst, ctx, dst)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930613__attribute__((always_inline)) static inline int
Andrew Jeffery66c77232024-04-24 11:42:02 +0930614// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930615pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030616{
617 int32_t ldst;
618
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930619 assert(ctx);
620
621 if (!ctx->cursor || !dst) {
622 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030623 }
624
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930625 static_assert(
626 // NOLINTNEXTLINE(bugprone-sizeof-expression)
627 sizeof(ldst) < INTMAX_MAX,
628 "The following addition may not uphold the runtime assertion");
629 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
630 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930631 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930632 }
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030633 ctx->remaining -= sizeof(ldst);
634 assert(ctx->remaining >= 0);
635 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930636 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030637 }
638
639 memcpy(&ldst, ctx->cursor, sizeof(ldst));
Andrew Jeffery66c77232024-04-24 11:42:02 +0930640 ldst = le32toh(ldst);
641 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030642 ctx->cursor += sizeof(ldst);
643
644 return PLDM_SUCCESS;
645}
646
Andrew Jeffery66c77232024-04-24 11:42:02 +0930647#define pldm_msgbuf_extract_real32(ctx, dst) \
648 pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \
649 dst, ctx, dst)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930650__attribute__((always_inline)) static inline int
Andrew Jeffery66c77232024-04-24 11:42:02 +0930651// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +0930652pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030653{
654 uint32_t ldst;
655
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930656 static_assert(sizeof(real32_t) == sizeof(ldst),
657 "Mismatched type sizes for dst and ldst");
Andrew Jeffery66c77232024-04-24 11:42:02 +0930658
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930659 assert(ctx);
660
661 if (!ctx->cursor || !dst) {
662 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030663 }
664
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930665 static_assert(
666 // NOLINTNEXTLINE(bugprone-sizeof-expression)
667 sizeof(ldst) < INTMAX_MAX,
668 "The following addition may not uphold the runtime assertion");
669 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
670 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930671 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930672 }
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030673 ctx->remaining -= sizeof(ldst);
674 assert(ctx->remaining >= 0);
675 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930676 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030677 }
678
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030679 memcpy(&ldst, ctx->cursor, sizeof(ldst));
680 ldst = le32toh(ldst);
Andrew Jeffery66c77232024-04-24 11:42:02 +0930681 memcpy(dst, &ldst, sizeof(ldst));
682 ctx->cursor += sizeof(ldst);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030683
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930684 return 0;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030685}
686
Andrew Jeffery66c77232024-04-24 11:42:02 +0930687/**
688 * Extract the field at the msgbuf cursor into the lvalue named by dst.
689 *
690 * @param ctx The msgbuf context object
691 * @param dst The lvalue into which the field at the msgbuf cursor should be
692 * extracted
693 *
694 * @return PLDM_SUCCESS on success, otherwise another value on error
695 */
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030696#define pldm_msgbuf_extract(ctx, dst) \
Andrew Jeffery66c77232024-04-24 11:42:02 +0930697 _Generic((dst), \
698 uint8_t: pldm__msgbuf_extract_uint8, \
699 int8_t: pldm__msgbuf_extract_int8, \
700 uint16_t: pldm__msgbuf_extract_uint16, \
701 int16_t: pldm__msgbuf_extract_int16, \
702 uint32_t: pldm__msgbuf_extract_uint32, \
703 int32_t: pldm__msgbuf_extract_int32, \
704 real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
705
706/**
707 * Extract the field at the msgbuf cursor into the object pointed-to by dst.
708 *
709 * @param ctx The msgbuf context object
710 * @param dst The pointer to the object into which the field at the msgbuf
711 * cursor should be extracted
712 *
713 * @return PLDM_SUCCESS on success, otherwise another value on error
714 */
715#define pldm_msgbuf_extract_p(ctx, dst) \
716 _Generic((dst), \
717 uint8_t *: pldm__msgbuf_extract_uint8, \
718 int8_t *: pldm__msgbuf_extract_int8, \
719 uint16_t *: pldm__msgbuf_extract_uint16, \
720 int16_t *: pldm__msgbuf_extract_int16, \
721 uint32_t *: pldm__msgbuf_extract_uint32, \
722 int32_t *: pldm__msgbuf_extract_int32, \
723 real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030724
Andrew Jeffery76712f62024-05-22 15:19:00 +0930725__attribute__((always_inline)) static inline int
726pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, uint8_t *dst,
727 size_t count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930728{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930729 assert(ctx);
730
731 if (!ctx->cursor || !dst) {
732 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jeffery369b1212023-04-20 15:44:48 +0930733 }
734
735 if (!count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930736 return 0;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930737 }
738
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930739#if INTMAX_MAX < SIZE_MAX
740 if (count > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930741 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery369b1212023-04-20 15:44:48 +0930742 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930743#endif
Andrew Jeffery369b1212023-04-20 15:44:48 +0930744
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930745 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930746 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930747 }
748 ctx->remaining -= (intmax_t)count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930749 assert(ctx->remaining >= 0);
750 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930751 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery369b1212023-04-20 15:44:48 +0930752 }
753
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030754 memcpy(dst, ctx->cursor, count);
755 ctx->cursor += count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930756
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930757 return 0;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930758}
759
760#define pldm_msgbuf_extract_array(ctx, dst, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930761 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
762 count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930763
Andrew Jeffery76712f62024-05-22 15:19:00 +0930764__attribute__((always_inline)) static inline int
765pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx, const uint32_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700766{
767 uint32_t val = htole32(src);
768
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930769 assert(ctx);
770
771 if (!ctx->cursor) {
772 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700773 }
774
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930775 static_assert(
776 // NOLINTNEXTLINE(bugprone-sizeof-expression)
777 sizeof(src) < INTMAX_MAX,
778 "The following addition may not uphold the runtime assertion");
779 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
780 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930781 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930782 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700783 ctx->remaining -= sizeof(src);
784 assert(ctx->remaining >= 0);
785 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930786 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700787 }
788
789 memcpy(ctx->cursor, &val, sizeof(val));
790 ctx->cursor += sizeof(src);
791
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930792 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700793}
794
Andrew Jeffery76712f62024-05-22 15:19:00 +0930795__attribute__((always_inline)) static inline int
796pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx, const uint16_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700797{
798 uint16_t val = htole16(src);
799
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930800 assert(ctx);
801
802 if (!ctx->cursor) {
803 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700804 }
805
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930806 static_assert(
807 // NOLINTNEXTLINE(bugprone-sizeof-expression)
808 sizeof(src) < INTMAX_MAX,
809 "The following addition may not uphold the runtime assertion");
810 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
811 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930812 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930813 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700814 ctx->remaining -= sizeof(src);
815 assert(ctx->remaining >= 0);
816 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930817 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700818 }
819
820 memcpy(ctx->cursor, &val, sizeof(val));
821 ctx->cursor += sizeof(src);
822
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930823 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700824}
825
Andrew Jeffery76712f62024-05-22 15:19:00 +0930826__attribute__((always_inline)) static inline int
827pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx, const uint8_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700828{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930829 assert(ctx);
830
831 if (!ctx->cursor) {
832 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700833 }
834
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930835 static_assert(
836 // NOLINTNEXTLINE(bugprone-sizeof-expression)
837 sizeof(src) < INTMAX_MAX,
838 "The following addition may not uphold the runtime assertion");
839 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
840 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930841 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930842 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700843 ctx->remaining -= sizeof(src);
844 assert(ctx->remaining >= 0);
845 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930846 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700847 }
848
849 memcpy(ctx->cursor, &src, sizeof(src));
850 ctx->cursor += sizeof(src);
851
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930852 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700853}
854
Andrew Jeffery76712f62024-05-22 15:19:00 +0930855__attribute__((always_inline)) static inline int
856pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx, const int32_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700857{
858 int32_t val = htole32(src);
859
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930860 assert(ctx);
861
862 if (!ctx->cursor) {
863 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700864 }
865
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930866 static_assert(
867 // NOLINTNEXTLINE(bugprone-sizeof-expression)
868 sizeof(src) < INTMAX_MAX,
869 "The following addition may not uphold the runtime assertion");
870 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
871 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930872 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930873 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700874 ctx->remaining -= sizeof(src);
875 assert(ctx->remaining >= 0);
876 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930877 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700878 }
879
880 memcpy(ctx->cursor, &val, sizeof(val));
881 ctx->cursor += sizeof(src);
882
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930883 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700884}
885
Andrew Jeffery76712f62024-05-22 15:19:00 +0930886__attribute__((always_inline)) static inline int
887pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx, const int16_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700888{
889 int16_t val = htole16(src);
890
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930891 assert(ctx);
892
893 if (!ctx->cursor) {
894 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700895 }
896
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930897 static_assert(
898 // NOLINTNEXTLINE(bugprone-sizeof-expression)
899 sizeof(src) < INTMAX_MAX,
900 "The following addition may not uphold the runtime assertion");
901 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
902 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930903 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930904 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700905 ctx->remaining -= sizeof(src);
906 assert(ctx->remaining >= 0);
907 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930908 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700909 }
910
911 memcpy(ctx->cursor, &val, sizeof(val));
912 ctx->cursor += sizeof(src);
913
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930914 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700915}
916
Andrew Jeffery76712f62024-05-22 15:19:00 +0930917__attribute__((always_inline)) static inline int
918pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx, const int8_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700919{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930920 assert(ctx);
921
922 if (!ctx->cursor) {
923 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700924 }
925
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930926 static_assert(
927 // NOLINTNEXTLINE(bugprone-sizeof-expression)
928 sizeof(src) < INTMAX_MAX,
929 "The following addition may not uphold the runtime assertion");
930 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
931 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930932 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930933 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700934 ctx->remaining -= sizeof(src);
935 assert(ctx->remaining >= 0);
936 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930937 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700938 }
939
940 memcpy(ctx->cursor, &src, sizeof(src));
941 ctx->cursor += sizeof(src);
942
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930943 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700944}
945
946#define pldm_msgbuf_insert(dst, src) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930947 _Generic((src), \
948 uint8_t: pldm_msgbuf_insert_uint8, \
949 int8_t: pldm_msgbuf_insert_int8, \
950 uint16_t: pldm_msgbuf_insert_uint16, \
951 int16_t: pldm_msgbuf_insert_int16, \
952 uint32_t: pldm_msgbuf_insert_uint32, \
953 int32_t: pldm_msgbuf_insert_int32)(dst, src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700954
Andrew Jeffery76712f62024-05-22 15:19:00 +0930955__attribute__((always_inline)) static inline int
956pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, const uint8_t *src,
957 size_t count)
Thu Nguyen062c8762023-04-22 20:45:04 +0700958{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930959 assert(ctx);
960
961 if (!ctx->cursor || !src) {
962 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700963 }
964
965 if (!count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930966 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700967 }
968
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930969#if INTMAX_MAX < SIZE_MAX
970 if (count > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930971 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700972 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930973#endif
Thu Nguyen062c8762023-04-22 20:45:04 +0700974
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930975 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930976 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930977 }
978 ctx->remaining -= (intmax_t)count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700979 assert(ctx->remaining >= 0);
980 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930981 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700982 }
983
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030984 memcpy(ctx->cursor, src, count);
985 ctx->cursor += count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700986
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930987 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700988}
989
990#define pldm_msgbuf_insert_array(dst, src, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930991 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \
992 count)
Thu Nguyen062c8762023-04-22 20:45:04 +0700993
Andrew Jeffery76712f62024-05-22 15:19:00 +0930994__attribute__((always_inline)) static inline int
995pldm_msgbuf_span_required(struct pldm_msgbuf *ctx, size_t required,
996 void **cursor)
Thu Nguyen062c8762023-04-22 20:45:04 +0700997{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930998 assert(ctx);
999
1000 if (!ctx->cursor || !cursor || *cursor) {
1001 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +07001002 }
1003
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301004#if INTMAX_MAX < SIZE_MAX
1005 if (required > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301006 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +07001007 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301008#endif
Thu Nguyen062c8762023-04-22 20:45:04 +07001009
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301010 if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301011 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301012 }
1013 ctx->remaining -= (intmax_t)required;
Thu Nguyen062c8762023-04-22 20:45:04 +07001014 assert(ctx->remaining >= 0);
1015 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301016 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +07001017 }
1018
1019 *cursor = ctx->cursor;
1020 ctx->cursor += required;
1021
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301022 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +07001023}
1024
Andrew Jeffery76712f62024-05-22 15:19:00 +09301025__attribute__((always_inline)) static inline int
1026pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
Thu Nguyen062c8762023-04-22 20:45:04 +07001027{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301028 assert(ctx);
1029
1030 if (!ctx->cursor || !cursor || *cursor || !len) {
1031 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +07001032 }
1033
1034 assert(ctx->remaining >= 0);
1035 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301036 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +07001037 }
1038
1039 *cursor = ctx->cursor;
1040 ctx->cursor += ctx->remaining;
1041 *len = ctx->remaining;
1042 ctx->remaining = 0;
1043
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301044 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +07001045}
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001046
1047/**
1048 * @brief pldm_msgbuf copy data between two msg buffers
1049 *
Manojkiran Eda9e3a5d42024-06-17 16:06:42 +05301050 * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1051 * @param[in,out] dst - destination of copy from source
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001052 * @param[in] size - size of data to be copied
1053 * @param[in] description - description of data copied
1054 *
1055 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1056 * PLDM_ERROR_INVALID_LENGTH otherwise.
1057 * PLDM_ERROR_INVALID_DATA if input is invalid
1058 */
1059#define pldm_msgbuf_copy(dst, src, type, name) \
1060 pldm__msgbuf_copy(dst, src, sizeof(type), #name)
Andrew Jeffery76712f62024-05-22 15:19:00 +09301061__attribute__((always_inline)) static inline int
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001062// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +09301063pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1064 const char *description)
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001065{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301066 assert(src);
1067 assert(dst);
1068 assert(src->mode == dst->mode);
1069
1070 if (!src->cursor || !dst->cursor || !description) {
1071 return pldm_msgbuf_status(dst, EINVAL);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001072 }
1073
1074#if INTMAX_MAX < SIZE_MAX
1075 if (size > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301076 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001077 }
1078#endif
1079
1080 if (src->remaining < INTMAX_MIN + (intmax_t)size) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301081 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001082 }
1083
1084 if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301085 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001086 }
1087
1088 src->remaining -= (intmax_t)size;
1089 assert(src->remaining >= 0);
1090 if (src->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301091 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001092 }
1093
1094 dst->remaining -= (intmax_t)size;
1095 assert(dst->remaining >= 0);
1096 if (dst->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301097 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001098 }
1099
1100 memcpy(dst->cursor, src->cursor, size);
1101 src->cursor += size;
1102 dst->cursor += size;
1103
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301104 return 0;
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001105}
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301106
Andrew Jefferyc63f63a2023-02-24 22:29:33 +10301107#ifdef __cplusplus
1108}
1109#endif
1110
Andrew Jeffery66c77232024-04-24 11:42:02 +09301111#ifdef __cplusplus
1112#include <type_traits>
1113
1114template <typename T>
1115static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
1116 void *buf)
1117{
1118 static_assert(std::is_same<uint8_t *, T>::value);
1119 return pldm__msgbuf_extract_uint8(ctx, buf);
1120}
1121
1122template <typename T>
1123static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
1124 void *buf)
1125{
1126 static_assert(std::is_same<int8_t *, T>::value);
1127 return pldm__msgbuf_extract_int8(ctx, buf);
1128}
1129
1130template <typename T>
1131static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
1132 void *buf)
1133{
1134 static_assert(std::is_same<uint16_t *, T>::value);
1135 return pldm__msgbuf_extract_uint16(ctx, buf);
1136}
1137
1138template <typename T>
1139static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
1140 void *buf)
1141{
1142 static_assert(std::is_same<int16_t *, T>::value);
1143 return pldm__msgbuf_extract_int16(ctx, buf);
1144}
1145
1146template <typename T>
1147static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
1148 void *buf)
1149{
1150 static_assert(std::is_same<uint32_t *, T>::value);
1151 return pldm__msgbuf_extract_uint32(ctx, buf);
1152}
1153
1154template <typename T>
1155static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
1156 void *buf)
1157{
1158 static_assert(std::is_same<int32_t *, T>::value);
1159 return pldm__msgbuf_extract_int32(ctx, buf);
1160}
1161
1162template <typename T>
1163static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
1164 void *buf)
1165{
1166 static_assert(std::is_same<real32_t *, T>::value);
1167 return pldm__msgbuf_extract_real32(ctx, buf);
1168}
1169#endif
1170
Andrew Jefferyc63f63a2023-02-24 22:29:33 +10301171#endif /* BUF_H */