blob: c3498bb17510f75f6ca2f6ae5cc61f404e6709a5 [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 Jeffery860a43d2024-08-23 01:21:58 +00005#include "compiler.h"
6
Andrew Jeffery66c77232024-04-24 11:42:02 +09307/*
8 * Historically, many of the structs exposed in libpldm's public headers are
9 * defined with __attribute__((packed)). This is unfortunate: it gives the
10 * impression that a wire-format buffer can be cast to the message type to make
11 * the message's fields easily accessible. As it turns out, that's not
12 * that's valid for several reasons:
13 *
14 * 1. Casting the wire-format buffer to a struct of the message type doesn't
15 * abstract the endianness of message field values
16 *
17 * 2. Some messages contain packed tagged union fields which cannot be properly
18 * described in a C struct.
19 *
20 * The msgbuf APIs exist to assist with (un)packing the wire-format in a way
21 * that is type-safe, spatially memory-safe, endian-safe, performant, and
22 * free of undefined-behaviour. Message structs that are added to the public
23 * library API should no-longer be marked __attribute__((packed)), and the
24 * implementation of their encode and decode functions must exploit the msgbuf
25 * API.
26 *
27 * However, we would like to allow implementation of codec functions in terms of
28 * msgbuf APIs even if they're decoding a message into a (historically) packed
29 * struct. Some of the complexity that follows is a consequence of the packed/
30 * unpacked conflict.
31 */
32
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103033#ifdef __cplusplus
Andrew Jeffery37dd6a32023-05-12 16:04:06 +093034/*
35 * Fix up C11's _Static_assert() vs C++'s static_assert().
36 *
37 * Can we please have nice things for once.
38 */
39// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
40#define _Static_assert(...) static_assert(__VA_ARGS__)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103041extern "C" {
42#endif
43
Andrew Jefferyb0c1d202023-11-07 22:08:44 +103044#include <libpldm/base.h>
45#include <libpldm/pldm_types.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103046
Andrew Jeffery66c77232024-04-24 11:42:02 +093047#include "compiler.h"
48
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103049#include <assert.h>
50#include <endian.h>
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093051#include <errno.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103052#include <limits.h>
53#include <stdbool.h>
Andrew Jeffery66c77232024-04-24 11:42:02 +093054#include <stdint.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103055#include <string.h>
56#include <sys/types.h>
Thu Nguyen15237782024-07-02 09:30:41 +000057#include <uchar.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103058
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093059/*
60 * We can't use static_assert() outside of some other C construct. Deal
61 * with high-level global assertions by burying them in an unused struct
62 * declaration, that has a sole member for compliance with the requirement that
63 * types must have a size.
64*/
65static struct {
66 static_assert(
67 INTMAX_MAX != SIZE_MAX,
68 "Extraction and insertion value comparisons may be broken");
69 static_assert(INTMAX_MIN + INTMAX_MAX <= 0,
70 "Extraction and insertion arithmetic may be broken");
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093071 static_assert(PLDM_SUCCESS == 0, "Error handling is broken");
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093072 int compliance;
Andrew Jeffery860a43d2024-08-23 01:21:58 +000073} build_assertions LIBPLDM_CC_UNUSED;
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093074
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093075enum pldm_msgbuf_error_mode {
76 PLDM_MSGBUF_PLDM_CC = 0x5a,
77 PLDM_MSGBUF_C_ERRNO = 0xa5,
78};
79
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103080struct pldm_msgbuf {
Thu Nguyen062c8762023-04-22 20:45:04 +070081 uint8_t *cursor;
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093082 intmax_t remaining;
Andrew Jefferyc8df31c2024-05-21 16:47:43 +093083 enum pldm_msgbuf_error_mode mode;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103084};
85
Andrew Jefferyd861a682024-06-03 21:43:09 +093086/**
87 * @brief Either negate an errno value or return a value mapped to a PLDM
88 * completion code.
89 *
90 * Note that `pldm_msgbuf_status()` is purely internal to the msgbuf API
91 * for ergonomics. It's preferred that we don't try to unify this with
92 * `pldm_xlate_errno()` from src/api.h despite the similarities.
93 *
94 * @param[in] ctx - The msgbuf context providing the personality info
95 * @param[in] err - The positive errno value to translate
96 *
97 * @return Either the negated value of @p err if the context's error mode is
98 * `PLDM_MSGBUF_C_ERRNO`, or the equivalent PLDM completion code if the
99 * error mode is `PLDM_MSGBUF_PLDM_CC`.
100 */
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300101LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_status(struct pldm_msgbuf *ctx,
102 unsigned int err)
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930103{
104 int rc;
105
106 assert(err != 0);
107 assert(err <= INT_MAX);
108
109 if (ctx->mode == PLDM_MSGBUF_C_ERRNO) {
110 if (err > INT_MAX) {
111 return -EINVAL;
112 }
113
114 static_assert(INT_MIN + INT_MAX < 0,
115 "Arithmetic assumption failure");
116 return -((int)err);
117 }
118
119 if (err > INT_MAX) {
120 return PLDM_ERROR;
121 }
122
123 assert(ctx->mode == PLDM_MSGBUF_PLDM_CC);
124 switch (err) {
125 case EINVAL:
126 rc = PLDM_ERROR_INVALID_DATA;
127 break;
128 case EBADMSG:
129 case EOVERFLOW:
130 rc = PLDM_ERROR_INVALID_LENGTH;
131 break;
132 default:
133 assert(false);
134 rc = PLDM_ERROR;
135 break;
136 }
137
138 assert(rc > 0);
139 return rc;
140}
141
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030142/**
143 * @brief Initialize pldm buf struct for buf extractor
144 *
145 * @param[out] ctx - pldm_msgbuf context for extractor
146 * @param[in] minsize - The minimum required length of buffer `buf`
147 * @param[in] buf - buffer to be extracted
148 * @param[in] len - size of buffer
149 *
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930150 * @return 0 on success, otherwise an error code appropriate for the current
151 * personality.
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030152 */
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300153LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930154// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
155pldm__msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
156 size_t len)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030157{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930158 assert(ctx);
159 assert(ctx->mode == PLDM_MSGBUF_PLDM_CC ||
160 ctx->mode == PLDM_MSGBUF_C_ERRNO);
161
162 if (!buf) {
163 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030164 }
165
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930166 if ((minsize > len)) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930167 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030168 }
169
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930170#if INTMAX_MAX < SIZE_MAX
171 if (len > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930172 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930173 }
174#endif
175
Andrew Jeffery07febdb2024-05-17 14:17:14 +0930176 if ((uintptr_t)buf + len < len) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930177 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030178 }
179
180 ctx->cursor = (uint8_t *)buf;
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930181 ctx->remaining = (intmax_t)len;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030182
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930183 return 0;
184}
185
186/**
187 * @brief Initialise a msgbuf instance to return errors as PLDM completion codes
188 *
189 * @see pldm__msgbuf_init
190 *
191 * @param[out] ctx - pldm_msgbuf context for extractor
192 * @param[in] minsize - The minimum required length of buffer `buf`
193 * @param[in] buf - buffer to be extracted
194 * @param[in] len - size of buffer
195 *
196 * @return PLDM_SUCCESS if the provided buffer region is sensible,
197 * otherwise PLDM_ERROR_INVALID_DATA if pointer parameters are invalid,
198 * or PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
199 */
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300200LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_init_cc(struct pldm_msgbuf *ctx,
201 size_t minsize,
202 const void *buf, size_t len)
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930203{
204 if (!ctx) {
205 return PLDM_ERROR_INVALID_DATA;
206 }
207
208 ctx->mode = PLDM_MSGBUF_PLDM_CC;
209 return pldm__msgbuf_init(ctx, minsize, buf, len);
210}
211
212/**
213 * @brief Initialise a msgbuf instance to return errors as negative errno values
214 *
215 * @see pldm__msgbuf_init
216 *
217 * @param[out] ctx - pldm_msgbuf context for extractor
218 * @param[in] minsize - The minimum required length of buffer `buf`
219 * @param[in] buf - buffer to be extracted
220 * @param[in] len - size of buffer
221 *
222 * @return 0 if the provided buffer region is sensible, otherwise -EINVAL if
223 * pointer parameters are invalid, or -EOVERFLOW if length constraints
224 * are violated.
225 */
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300226LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_init_errno(struct pldm_msgbuf *ctx,
227 size_t minsize,
228 const void *buf, size_t len)
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930229{
230 if (!ctx) {
231 return -EINVAL;
232 }
233
234 ctx->mode = PLDM_MSGBUF_C_ERRNO;
235 return pldm__msgbuf_init(ctx, minsize, buf, len);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030236}
237
238/**
239 * @brief Validate buffer overflow state
240 *
241 * @param[in] ctx - pldm_msgbuf context for extractor
242 *
243 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
244 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
245 * prior accesses would have occurred beyond the bounds of the buffer, and
246 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
247 * pointer.
248 */
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300249LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030250{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930251 assert(ctx);
252 if (ctx->remaining < 0) {
253 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030254 }
255
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930256 return 0;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030257}
258
259/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930260 * @brief Test whether a message buffer has been exactly consumed
261 *
262 * @param[in] ctx - pldm_msgbuf context for extractor
263 *
264 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
265 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
266 * indicates that an incorrect sequence of accesses have occurred, and
267 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
268 * pointer.
269 */
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300270LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930271{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930272 assert(ctx);
273 if (ctx->remaining != 0) {
274 return pldm_msgbuf_status(ctx, EBADMSG);
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930275 }
276
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930277 return 0;
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930278}
279
280/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030281 * @brief Destroy the pldm buf
282 *
283 * @param[in] ctx - pldm_msgbuf context for extractor
284 *
285 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
286 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
287 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
288 * bounds of the buffer.
289 */
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300290LIBPLDM_CC_ALWAYS_INLINE int pldm_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 Jefferycb569bc2024-09-01 09:38:09 +0300314LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery76712f62024-05-22 15:19:00 +0930315pldm_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 Jefferycb569bc2024-09-01 09:38:09 +0300428LIBPLDM_CC_ALWAYS_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 Jefferycb569bc2024-09-01 09:38:09 +0300457LIBPLDM_CC_ALWAYS_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 Jefferycb569bc2024-09-01 09:38:09 +0300485LIBPLDM_CC_ALWAYS_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 Jefferycb569bc2024-09-01 09:38:09 +0300538LIBPLDM_CC_ALWAYS_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 Jefferycb569bc2024-09-01 09:38:09 +0300576LIBPLDM_CC_ALWAYS_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 Jefferycb569bc2024-09-01 09:38:09 +0300613LIBPLDM_CC_ALWAYS_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 Jefferycb569bc2024-09-01 09:38:09 +0300650LIBPLDM_CC_ALWAYS_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 Jeffery0a1be3c2024-08-11 08:34:10 +0000725/**
726 * @ref pldm_msgbuf_extract_array
727 */
728LIBPLDM_CC_WARN_UNUSED_RESULT
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300729LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery1c571442024-07-08 10:25:48 +0930730// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000731pldm__msgbuf_extract_array_void(struct pldm_msgbuf *ctx, size_t count,
732 void *dst, size_t dst_count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930733{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930734 assert(ctx);
735
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000736 if (!ctx->cursor || !dst || count > dst_count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930737 return pldm_msgbuf_status(ctx, EINVAL);
Andrew Jeffery369b1212023-04-20 15:44:48 +0930738 }
739
740 if (!count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930741 return 0;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930742 }
743
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930744#if INTMAX_MAX < SIZE_MAX
745 if (count > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930746 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery369b1212023-04-20 15:44:48 +0930747 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930748#endif
Andrew Jeffery369b1212023-04-20 15:44:48 +0930749
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930750 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930751 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930752 }
753 ctx->remaining -= (intmax_t)count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930754 assert(ctx->remaining >= 0);
755 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930756 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery369b1212023-04-20 15:44:48 +0930757 }
758
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030759 memcpy(dst, ctx->cursor, count);
760 ctx->cursor += count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930761
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930762 return 0;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930763}
764
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000765/**
766 * @ref pldm_msgbuf_extract_array
767 */
768LIBPLDM_CC_WARN_UNUSED_RESULT
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300769LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000770pldm_msgbuf_extract_array_char(struct pldm_msgbuf *ctx, size_t count, char *dst,
771 size_t dst_count)
Andrew Jeffery1c571442024-07-08 10:25:48 +0930772{
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000773 return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count);
Andrew Jeffery1c571442024-07-08 10:25:48 +0930774}
775
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000776/**
777 * @ref pldm_msgbuf_extract_array
778 */
779LIBPLDM_CC_WARN_UNUSED_RESULT
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300780LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000781pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx, size_t count,
782 uint8_t *dst, size_t dst_count)
Andrew Jeffery1c571442024-07-08 10:25:48 +0930783{
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000784 return pldm__msgbuf_extract_array_void(ctx, count, dst, dst_count);
Andrew Jeffery1c571442024-07-08 10:25:48 +0930785}
786
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000787/**
788 * Extract an array of data from the msgbuf instance
789 *
790 * @param ctx - The msgbuf instance from which to extract an array of data
791 * @param count - The number of array elements to extract
792 * @param dst - The array object into which elements from @p ctx should be
793 extracted
794 * @param dst_count - The maximum number of elements to place into @p dst
795 *
796 * Note that both @p count and @p dst_count can only be counted by `sizeof` for
797 * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number
798 * of array elements and _not_ the object size of the array.
799 */
800#define pldm_msgbuf_extract_array(ctx, count, dst, dst_count) \
Andrew Jeffery1c571442024-07-08 10:25:48 +0930801 _Generic((*(dst)), \
802 uint8_t: pldm_msgbuf_extract_array_uint8, \
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000803 char: pldm_msgbuf_extract_array_char)(ctx, count, dst, \
804 dst_count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930805
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300806LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
807 const uint32_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700808{
809 uint32_t val = htole32(src);
810
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930811 assert(ctx);
812
813 if (!ctx->cursor) {
814 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700815 }
816
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930817 static_assert(
818 // NOLINTNEXTLINE(bugprone-sizeof-expression)
819 sizeof(src) < INTMAX_MAX,
820 "The following addition may not uphold the runtime assertion");
821 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
822 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930823 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930824 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700825 ctx->remaining -= sizeof(src);
826 assert(ctx->remaining >= 0);
827 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930828 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700829 }
830
831 memcpy(ctx->cursor, &val, sizeof(val));
832 ctx->cursor += sizeof(src);
833
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930834 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700835}
836
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300837LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
838 const uint16_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700839{
840 uint16_t val = htole16(src);
841
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930842 assert(ctx);
843
844 if (!ctx->cursor) {
845 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700846 }
847
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930848 static_assert(
849 // NOLINTNEXTLINE(bugprone-sizeof-expression)
850 sizeof(src) < INTMAX_MAX,
851 "The following addition may not uphold the runtime assertion");
852 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
853 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930854 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930855 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700856 ctx->remaining -= sizeof(src);
857 assert(ctx->remaining >= 0);
858 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930859 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700860 }
861
862 memcpy(ctx->cursor, &val, sizeof(val));
863 ctx->cursor += sizeof(src);
864
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930865 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700866}
867
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300868LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
869 const uint8_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700870{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930871 assert(ctx);
872
873 if (!ctx->cursor) {
874 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700875 }
876
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930877 static_assert(
878 // NOLINTNEXTLINE(bugprone-sizeof-expression)
879 sizeof(src) < INTMAX_MAX,
880 "The following addition may not uphold the runtime assertion");
881 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
882 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930883 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930884 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700885 ctx->remaining -= sizeof(src);
886 assert(ctx->remaining >= 0);
887 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930888 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700889 }
890
891 memcpy(ctx->cursor, &src, sizeof(src));
892 ctx->cursor += sizeof(src);
893
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930894 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700895}
896
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300897LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
898 const int32_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700899{
900 int32_t val = htole32(src);
901
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930902 assert(ctx);
903
904 if (!ctx->cursor) {
905 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700906 }
907
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930908 static_assert(
909 // NOLINTNEXTLINE(bugprone-sizeof-expression)
910 sizeof(src) < INTMAX_MAX,
911 "The following addition may not uphold the runtime assertion");
912 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
913 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930914 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930915 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700916 ctx->remaining -= sizeof(src);
917 assert(ctx->remaining >= 0);
918 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930919 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700920 }
921
922 memcpy(ctx->cursor, &val, sizeof(val));
923 ctx->cursor += sizeof(src);
924
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930925 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700926}
927
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300928LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
929 const int16_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700930{
931 int16_t val = htole16(src);
932
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930933 assert(ctx);
934
935 if (!ctx->cursor) {
936 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700937 }
938
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930939 static_assert(
940 // NOLINTNEXTLINE(bugprone-sizeof-expression)
941 sizeof(src) < INTMAX_MAX,
942 "The following addition may not uphold the runtime assertion");
943 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
944 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930945 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930946 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700947 ctx->remaining -= sizeof(src);
948 assert(ctx->remaining >= 0);
949 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930950 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700951 }
952
953 memcpy(ctx->cursor, &val, sizeof(val));
954 ctx->cursor += sizeof(src);
955
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930956 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700957}
958
Andrew Jefferycb569bc2024-09-01 09:38:09 +0300959LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
960 const int8_t src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700961{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930962 assert(ctx);
963
964 if (!ctx->cursor) {
965 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +0700966 }
967
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930968 static_assert(
969 // NOLINTNEXTLINE(bugprone-sizeof-expression)
970 sizeof(src) < INTMAX_MAX,
971 "The following addition may not uphold the runtime assertion");
972 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
973 assert(ctx->remaining < 0);
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930974 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930975 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700976 ctx->remaining -= sizeof(src);
977 assert(ctx->remaining >= 0);
978 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930979 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +0700980 }
981
982 memcpy(ctx->cursor, &src, sizeof(src));
983 ctx->cursor += sizeof(src);
984
Andrew Jefferyc8df31c2024-05-21 16:47:43 +0930985 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +0700986}
987
988#define pldm_msgbuf_insert(dst, src) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930989 _Generic((src), \
990 uint8_t: pldm_msgbuf_insert_uint8, \
991 int8_t: pldm_msgbuf_insert_int8, \
992 uint16_t: pldm_msgbuf_insert_uint16, \
993 int16_t: pldm_msgbuf_insert_int16, \
994 uint32_t: pldm_msgbuf_insert_uint32, \
995 int32_t: pldm_msgbuf_insert_int32)(dst, src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700996
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +0000997/**
998 * @ref pldm_msgbuf_insert_array
999 */
1000LIBPLDM_CC_WARN_UNUSED_RESULT
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001001LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery1c571442024-07-08 10:25:48 +09301002// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001003pldm__msgbuf_insert_array_void(struct pldm_msgbuf *ctx, size_t count,
1004 const void *src, size_t src_count)
Thu Nguyen062c8762023-04-22 20:45:04 +07001005{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301006 assert(ctx);
1007
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001008 if (!ctx->cursor || !src || count > src_count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301009 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +07001010 }
1011
1012 if (!count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301013 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +07001014 }
1015
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301016#if INTMAX_MAX < SIZE_MAX
1017 if (count > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301018 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +07001019 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301020#endif
Thu Nguyen062c8762023-04-22 20:45:04 +07001021
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301022 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301023 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301024 }
1025 ctx->remaining -= (intmax_t)count;
Thu Nguyen062c8762023-04-22 20:45:04 +07001026 assert(ctx->remaining >= 0);
1027 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301028 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +07001029 }
1030
Andrew Jefferya065ecc2023-10-27 15:02:11 +10301031 memcpy(ctx->cursor, src, count);
1032 ctx->cursor += count;
Thu Nguyen062c8762023-04-22 20:45:04 +07001033
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301034 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +07001035}
1036
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001037/**
1038 * @ref pldm_msgbuf_insert_array
1039 */
1040LIBPLDM_CC_WARN_UNUSED_RESULT
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001041LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001042pldm_msgbuf_insert_array_char(struct pldm_msgbuf *ctx, size_t count,
1043 const char *src, size_t src_count)
Andrew Jeffery1c571442024-07-08 10:25:48 +09301044{
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001045 return pldm__msgbuf_insert_array_void(ctx, count, src, src_count);
Andrew Jeffery1c571442024-07-08 10:25:48 +09301046}
1047
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001048/**
1049 * @ref pldm_msgbuf_insert_array
1050 */
1051LIBPLDM_CC_WARN_UNUSED_RESULT
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001052LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001053pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx, size_t count,
1054 const uint8_t *src, size_t src_count)
Andrew Jeffery1c571442024-07-08 10:25:48 +09301055{
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001056 return pldm__msgbuf_insert_array_void(ctx, count, src, src_count);
Andrew Jeffery1c571442024-07-08 10:25:48 +09301057}
1058
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001059/**
1060 * Insert an array of data into the msgbuf instance
1061 *
1062 * @param ctx - The msgbuf instance into which the array of data should be
1063 * inserted
1064 * @param count - The number of array elements to insert
1065 * @param src - The array object from which elements should be inserted into
1066 @p ctx
1067 * @param src_count - The maximum number of elements to insert from @p src
1068 *
1069 * Note that both @p count and @p src_count can only be counted by `sizeof` for
1070 * arrays where `sizeof(*dst) == 1` holds. Specifically, they count the number
1071 * of array elements and _not_ the object size of the array.
1072 */
1073#define pldm_msgbuf_insert_array(dst, count, src, src_count) \
Andrew Jeffery1c571442024-07-08 10:25:48 +09301074 _Generic((*(src)), \
1075 uint8_t: pldm_msgbuf_insert_array_uint8, \
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001076 char: pldm_msgbuf_insert_array_char)(dst, count, src, \
1077 src_count)
Thu Nguyen062c8762023-04-22 20:45:04 +07001078
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001079LIBPLDM_CC_ALWAYS_INLINE int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
1080 size_t required,
1081 void **cursor)
Thu Nguyen062c8762023-04-22 20:45:04 +07001082{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301083 assert(ctx);
1084
1085 if (!ctx->cursor || !cursor || *cursor) {
1086 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +07001087 }
1088
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301089#if INTMAX_MAX < SIZE_MAX
1090 if (required > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301091 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +07001092 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301093#endif
Thu Nguyen062c8762023-04-22 20:45:04 +07001094
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301095 if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301096 return pldm_msgbuf_status(ctx, EOVERFLOW);
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +09301097 }
1098 ctx->remaining -= (intmax_t)required;
Thu Nguyen062c8762023-04-22 20:45:04 +07001099 assert(ctx->remaining >= 0);
1100 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301101 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +07001102 }
1103
1104 *cursor = ctx->cursor;
1105 ctx->cursor += required;
1106
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301107 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +07001108}
1109
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001110LIBPLDM_CC_ALWAYS_INLINE int
Thu Nguyen9c83d682024-07-02 08:43:09 +00001111pldm_msgbuf_span_string_ascii(struct pldm_msgbuf *ctx, void **cursor,
1112 size_t *length)
1113{
1114 intmax_t measured;
1115
1116 assert(ctx);
1117
1118 if (!ctx->cursor || (cursor && *cursor)) {
1119 return pldm_msgbuf_status(ctx, EINVAL);
1120 }
1121
1122 if (ctx->remaining < 0) {
1123 /* Tracking the amount of overflow gets disturbed here */
1124 return pldm_msgbuf_status(ctx, EOVERFLOW);
1125 }
1126
1127 measured = (intmax_t)strnlen((const char *)ctx->cursor, ctx->remaining);
1128 if (measured == ctx->remaining) {
1129 /*
1130 * We have hit the end of the buffer prior to the NUL terminator.
1131 * Optimistically, the NUL terminator was one-beyond-the-end. Setting
1132 * ctx->remaining negative ensures the `pldm_msgbuf_destroy*()` APIs also
1133 * return an error.
1134 */
1135 ctx->remaining = -1;
1136 return pldm_msgbuf_status(ctx, EOVERFLOW);
1137 }
1138
1139 /* Include the NUL terminator in the span length, as spans are opaque */
1140 measured++;
1141
1142 if (ctx->remaining < INTMAX_MIN + measured) {
1143 return pldm_msgbuf_status(ctx, EOVERFLOW);
1144 }
1145
1146 ctx->remaining -= measured;
1147 assert(ctx->remaining >= 0);
1148 if (ctx->remaining < 0) {
1149 return pldm_msgbuf_status(ctx, EOVERFLOW);
1150 }
1151
1152 if (cursor) {
1153 *cursor = ctx->cursor;
1154 }
1155
1156 ctx->cursor += measured;
1157
1158 if (length) {
1159 *length = measured;
1160 }
1161
1162 return 0;
1163}
1164
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001165LIBPLDM_CC_ALWAYS_INLINE int
Thu Nguyen15237782024-07-02 09:30:41 +00001166pldm_msgbuf_span_string_utf16(struct pldm_msgbuf *ctx, void **cursor,
1167 size_t *length)
1168{
1169 static const char16_t term = 0;
1170 ptrdiff_t measured;
1171 void *end;
1172
1173 assert(ctx);
1174
1175 if (!ctx->cursor || (cursor && *cursor)) {
1176 return pldm_msgbuf_status(ctx, EINVAL);
1177 }
1178
1179 if (ctx->remaining < 0) {
1180 /* Tracking the amount of overflow gets disturbed here */
1181 return pldm_msgbuf_status(ctx, EOVERFLOW);
1182 }
1183
1184 /*
1185 * Avoid tripping up on UTF16-LE: We may have consecutive NUL _bytes_ that do
1186 * not form a UTF16 NUL _code-point_ due to alignment with respect to the
1187 * start of the string
1188 */
Andrew Jeffery2b440d42024-07-25 10:36:08 +09301189 end = ctx->cursor;
Thu Nguyen15237782024-07-02 09:30:41 +00001190 do {
1191 if (end != ctx->cursor) {
1192 /*
1193 * If we've looped we've found a relatively-unaligned NUL code-point.
1194 * Scan again from a relatively-aligned start point.
1195 */
1196 end = (char *)end + 1;
1197 }
1198 measured = (char *)end - (char *)ctx->cursor;
Andrew Jeffery2b440d42024-07-25 10:36:08 +09301199 end = memmem(end, ctx->remaining - measured, &term,
1200 sizeof(term));
Thu Nguyen15237782024-07-02 09:30:41 +00001201 } while (end && ((uintptr_t)end & 1) != ((uintptr_t)ctx->cursor & 1));
1202
1203 if (!end) {
1204 /*
1205 * Optimistically, the last required pattern byte was one beyond the end of
1206 * the buffer. Setting ctx->remaining negative ensures the
1207 * `pldm_msgbuf_destroy*()` APIs also return an error.
1208 */
1209 ctx->remaining = -1;
1210 return pldm_msgbuf_status(ctx, EOVERFLOW);
1211 }
1212
1213 end = (char *)end + sizeof(char16_t);
1214 measured = (char *)end - (char *)ctx->cursor;
1215
1216#if INTMAX_MAX < PTRDIFF_MAX
1217 if (measured >= INTMAX_MAX) {
1218 return pldm_msgbuf_status(ctx, EOVERFLOW);
1219 }
1220#endif
1221
1222 if (ctx->remaining < INTMAX_MIN + (intmax_t)measured) {
1223 assert(ctx->remaining < 0);
1224 return pldm_msgbuf_status(ctx, EOVERFLOW);
1225 }
1226
1227 ctx->remaining -= (intmax_t)measured;
1228 assert(ctx->remaining >= 0);
1229 if (ctx->remaining < 0) {
1230 return pldm_msgbuf_status(ctx, EOVERFLOW);
1231 }
1232
1233 if (cursor) {
1234 *cursor = ctx->cursor;
1235 }
1236
1237 ctx->cursor += measured;
1238
1239 if (length) {
1240 *length = (size_t)measured;
1241 }
1242
1243 return 0;
1244}
1245
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001246LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery76712f62024-05-22 15:19:00 +09301247pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx, void **cursor, size_t *len)
Thu Nguyen062c8762023-04-22 20:45:04 +07001248{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301249 assert(ctx);
1250
1251 if (!ctx->cursor || !cursor || *cursor || !len) {
1252 return pldm_msgbuf_status(ctx, EINVAL);
Thu Nguyen062c8762023-04-22 20:45:04 +07001253 }
1254
1255 assert(ctx->remaining >= 0);
1256 if (ctx->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301257 return pldm_msgbuf_status(ctx, EOVERFLOW);
Thu Nguyen062c8762023-04-22 20:45:04 +07001258 }
1259
1260 *cursor = ctx->cursor;
1261 ctx->cursor += ctx->remaining;
1262 *len = ctx->remaining;
1263 ctx->remaining = 0;
1264
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301265 return 0;
Thu Nguyen062c8762023-04-22 20:45:04 +07001266}
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001267
1268/**
1269 * @brief pldm_msgbuf copy data between two msg buffers
1270 *
Manojkiran Eda9e3a5d42024-06-17 16:06:42 +05301271 * @param[in,out] src - pldm_msgbuf for source from where value should be copied
1272 * @param[in,out] dst - destination of copy from source
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001273 * @param[in] size - size of data to be copied
1274 * @param[in] description - description of data copied
1275 *
1276 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
1277 * PLDM_ERROR_INVALID_LENGTH otherwise.
1278 * PLDM_ERROR_INVALID_DATA if input is invalid
1279 */
1280#define pldm_msgbuf_copy(dst, src, type, name) \
1281 pldm__msgbuf_copy(dst, src, sizeof(type), #name)
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001282LIBPLDM_CC_ALWAYS_INLINE int
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001283// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
Andrew Jeffery76712f62024-05-22 15:19:00 +09301284pldm__msgbuf_copy(struct pldm_msgbuf *dst, struct pldm_msgbuf *src, size_t size,
1285 const char *description)
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001286{
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301287 assert(src);
1288 assert(dst);
1289 assert(src->mode == dst->mode);
1290
1291 if (!src->cursor || !dst->cursor || !description) {
1292 return pldm_msgbuf_status(dst, EINVAL);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001293 }
1294
1295#if INTMAX_MAX < SIZE_MAX
1296 if (size > INTMAX_MAX) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301297 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001298 }
1299#endif
1300
1301 if (src->remaining < INTMAX_MIN + (intmax_t)size) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301302 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001303 }
1304
1305 if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301306 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001307 }
1308
1309 src->remaining -= (intmax_t)size;
1310 assert(src->remaining >= 0);
1311 if (src->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301312 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001313 }
1314
1315 dst->remaining -= (intmax_t)size;
1316 assert(dst->remaining >= 0);
1317 if (dst->remaining < 0) {
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301318 return pldm_msgbuf_status(dst, EOVERFLOW);
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001319 }
1320
1321 memcpy(dst->cursor, src->cursor, size);
1322 src->cursor += size;
1323 dst->cursor += size;
1324
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301325 return 0;
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -05001326}
Andrew Jefferyc8df31c2024-05-21 16:47:43 +09301327
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001328LIBPLDM_CC_WARN_UNUSED_RESULT
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001329LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery8b879602024-07-08 12:50:19 +09301330pldm_msgbuf_copy_string_ascii(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1331{
1332 void *ascii = NULL;
1333 size_t len = 0;
1334 int rc;
1335
1336 rc = pldm_msgbuf_span_string_ascii(src, &ascii, &len);
1337 if (rc < 0) {
1338 return rc;
1339 }
1340
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001341 return pldm__msgbuf_insert_array_void(dst, len, ascii, len);
Andrew Jeffery8b879602024-07-08 12:50:19 +09301342}
1343
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001344LIBPLDM_CC_WARN_UNUSED_RESULT
Andrew Jefferycb569bc2024-09-01 09:38:09 +03001345LIBPLDM_CC_ALWAYS_INLINE int
Andrew Jeffery56f73f92024-07-08 12:50:28 +09301346pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
1347{
1348 void *utf16 = NULL;
1349 size_t len = 0;
1350 int rc;
1351
1352 rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len);
1353 if (rc < 0) {
1354 return rc;
1355 }
1356
Andrew Jeffery0a1be3c2024-08-11 08:34:10 +00001357 return pldm__msgbuf_insert_array_void(dst, len, utf16, len);
Andrew Jeffery56f73f92024-07-08 12:50:28 +09301358}
1359
Andrew Jefferyc63f63a2023-02-24 22:29:33 +10301360#ifdef __cplusplus
1361}
1362#endif
1363
Andrew Jeffery66c77232024-04-24 11:42:02 +09301364#ifdef __cplusplus
1365#include <type_traits>
1366
1367template <typename T>
1368static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
1369 void *buf)
1370{
1371 static_assert(std::is_same<uint8_t *, T>::value);
1372 return pldm__msgbuf_extract_uint8(ctx, buf);
1373}
1374
1375template <typename T>
1376static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
1377 void *buf)
1378{
1379 static_assert(std::is_same<int8_t *, T>::value);
1380 return pldm__msgbuf_extract_int8(ctx, buf);
1381}
1382
1383template <typename T>
1384static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
1385 void *buf)
1386{
1387 static_assert(std::is_same<uint16_t *, T>::value);
1388 return pldm__msgbuf_extract_uint16(ctx, buf);
1389}
1390
1391template <typename T>
1392static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
1393 void *buf)
1394{
1395 static_assert(std::is_same<int16_t *, T>::value);
1396 return pldm__msgbuf_extract_int16(ctx, buf);
1397}
1398
1399template <typename T>
1400static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
1401 void *buf)
1402{
1403 static_assert(std::is_same<uint32_t *, T>::value);
1404 return pldm__msgbuf_extract_uint32(ctx, buf);
1405}
1406
1407template <typename T>
1408static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
1409 void *buf)
1410{
1411 static_assert(std::is_same<int32_t *, T>::value);
1412 return pldm__msgbuf_extract_int32(ctx, buf);
1413}
1414
1415template <typename T>
1416static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
1417 void *buf)
1418{
1419 static_assert(std::is_same<real32_t *, T>::value);
1420 return pldm__msgbuf_extract_real32(ctx, buf);
1421}
1422#endif
1423
Andrew Jefferyc63f63a2023-02-24 22:29:33 +10301424#endif /* BUF_H */