blob: 1f71e4c3ba05dee305c6693c929b9149ee7f27bf [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>
49#include <limits.h>
50#include <stdbool.h>
Andrew Jeffery66c77232024-04-24 11:42:02 +093051#include <stdint.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103052#include <string.h>
53#include <sys/types.h>
54
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093055/*
56 * We can't use static_assert() outside of some other C construct. Deal
57 * with high-level global assertions by burying them in an unused struct
58 * declaration, that has a sole member for compliance with the requirement that
59 * types must have a size.
60*/
61static struct {
62 static_assert(
63 INTMAX_MAX != SIZE_MAX,
64 "Extraction and insertion value comparisons may be broken");
65 static_assert(INTMAX_MIN + INTMAX_MAX <= 0,
66 "Extraction and insertion arithmetic may be broken");
67 int compliance;
68} build_assertions __attribute__((unused));
69
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103070struct pldm_msgbuf {
Thu Nguyen062c8762023-04-22 20:45:04 +070071 uint8_t *cursor;
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093072 intmax_t remaining;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103073};
74
75/**
76 * @brief Initialize pldm buf struct for buf extractor
77 *
78 * @param[out] ctx - pldm_msgbuf context for extractor
79 * @param[in] minsize - The minimum required length of buffer `buf`
80 * @param[in] buf - buffer to be extracted
81 * @param[in] len - size of buffer
82 *
83 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
84 * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
85 * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
86 */
Andrew Jeffery07febdb2024-05-17 14:17:14 +093087static inline int pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize,
88 const void *buf, size_t len)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103089{
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103090 if (!ctx || !buf) {
91 return PLDM_ERROR_INVALID_DATA;
92 }
93
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093094 if ((minsize > len)) {
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103095 return PLDM_ERROR_INVALID_LENGTH;
96 }
97
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +093098#if INTMAX_MAX < SIZE_MAX
99 if (len > INTMAX_MAX) {
100 return PLDM_ERROR_INVALID_LENGTH;
101 }
102#endif
103
Andrew Jeffery07febdb2024-05-17 14:17:14 +0930104 if ((uintptr_t)buf + len < len) {
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030105 return PLDM_ERROR_INVALID_LENGTH;
106 }
107
108 ctx->cursor = (uint8_t *)buf;
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930109 ctx->remaining = (intmax_t)len;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030110
111 return PLDM_SUCCESS;
112}
113
114/**
115 * @brief Validate buffer overflow state
116 *
117 * @param[in] ctx - pldm_msgbuf context for extractor
118 *
119 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
120 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
121 * prior accesses would have occurred beyond the bounds of the buffer, and
122 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
123 * pointer.
124 */
125static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
126{
127 if (!ctx) {
128 return PLDM_ERROR_INVALID_DATA;
129 }
130
131 return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
132}
133
134/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930135 * @brief Test whether a message buffer has been exactly consumed
136 *
137 * @param[in] ctx - pldm_msgbuf context for extractor
138 *
139 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
140 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
141 * indicates that an incorrect sequence of accesses have occurred, and
142 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
143 * pointer.
144 */
145static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
146{
147 if (!ctx) {
148 return PLDM_ERROR_INVALID_DATA;
149 }
150
151 return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
152}
153
154/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030155 * @brief Destroy the pldm buf
156 *
157 * @param[in] ctx - pldm_msgbuf context for extractor
158 *
159 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
160 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
161 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
162 * bounds of the buffer.
163 */
164static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
165{
166 int valid;
167
168 if (!ctx) {
169 return PLDM_ERROR_INVALID_DATA;
170 }
171
172 valid = pldm_msgbuf_validate(ctx);
173
174 ctx->cursor = NULL;
175 ctx->remaining = 0;
176
177 return valid;
178}
179
180/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930181 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
182 * has been completely consumed without overflow
183 *
184 * @param[in] ctx - pldm_msgbuf context
185 *
186 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
187 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
188 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
189 * have occurred byond the bounds of the buffer
190 */
191static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
192{
193 int consumed;
194
195 if (!ctx) {
196 return PLDM_ERROR_INVALID_DATA;
197 }
198
199 consumed = pldm_msgbuf_consumed(ctx);
200
201 ctx->cursor = NULL;
202 ctx->remaining = 0;
203
204 return consumed;
205}
206
Andrew Jeffery66c77232024-04-24 11:42:02 +0930207/*
208 * Exploit the pre-processor to perform type checking by macro substitution.
209 *
210 * A C type is defined by its alignment as well as its object
211 * size, and compilers have a hammer to enforce it in the form of
212 * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
213 * the libpldm public API this presents a problem: Naively attempting to use the
214 * msgbuf APIs on a member of a packed struct would yield an error.
215 *
216 * The msgbuf APIs are implemented such that data is moved through unaligned
217 * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
218 * make the object pointers take a trip through `void *` at its API boundary.
219 * That presents a bit too much of an opportunity to non-surgically remove your
220 * own foot, so here we set about doing something to mitigate that as well.
221 *
222 * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
223 * only for the purpose of object sizes, disregarding alignment. We have a few
224 * constraints that cause some headaches:
225 *
226 * 1. We have to perform the type-check before a call through a C function,
227 * as the function must take the object pointer argument as `void *`.
228 * Essentially, this constrains us to doing something with macros.
229 *
230 * 2. While libpldm is a C library, its test suite is written in C++ to take
231 * advantage of gtest.
232 *
233 * 3. Ideally we'd do something with C's `static_assert()`, however
234 * `static_assert()` is defined as void, and as we're constrained to macros,
235 * using `static_assert()` would require a statement-expression
236 *
237 * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
238 * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
239 * the purpose of enabling statement-expressions in this one instance.
240 *
241 * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
242 * however it's implemented in terms of `_Generic()`, which is not available
243 * in C++.
244 *
245 * Combined this means we need separate solutions for C and C++.
246 *
247 * For C, as we don't have statement-expressions, we need to exploit some other
248 * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
249 * API function call. We also have to take care of the fact that the call-sites
250 * may be in the context of a variable assignment for error-handling purposes.
251 * The key observation is that we can use the comma operator as a sequence point
252 * to order the type check before the API call, discarding the "result" value of
253 * the type check and yielding the return value of the API call.
254 *
255 * C++ could be less of a headache than the C as we can leverage template
256 * functions. An advantage of template functions is that while their definition
257 * is driven by instantion, the definition does not appear at the source
258 * location of the instantation, which gives it a great leg-up over the problems
259 * we have in the C path. However, the use of the msgbuf APIs in the test suite
260 * still makes things somewhat tricky, as the call-sites in the test suite are
261 * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
262 * takes both the object type and the required type as template arguments, and
263 * then define the object pointer parameter as `void *` for a call through to
264 * the appropriate msgbuf API. However, because the msgbuf API call-sites are
265 * encapsulated in gtest macros, use of commas in the template specification
266 * causes pre-processor confusion. In this way we're constrained to only one
267 * template argument per function.
268 *
269 * Implement the C++ path using template functions that take the destination
270 * object type as a template argument, while the name of the function symbols
271 * are derived from the required type. The manual implementations of these
272 * appear at the end of the header. The type safety is actually enforced
273 * by `static_assert()` this time, as we can use statements as we're not
274 * constrained to an expression in the templated function body.
275 *
276 * The invocations of pldm_msgbuf_extract_typecheck() typically result in
277 * double-evaluation of some arguments. We're not yet bothered by this for two
278 * reasons:
279 *
280 * 1. The nature of the current call-sites are such that there are no
281 * argument expressions that result in undesirable side-effects
282 *
283 * 2. It's an API internal to the libpldm implementation, and we can fix things
284 * whenever something crops up the violates the observation in 1.
285 */
286#ifdef __cplusplus
287#define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
288 pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
289#else
290#define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
291 (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
292#endif
293
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930294/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030295 * @brief pldm_msgbuf extractor for a uint8_t
296 *
297 * @param[inout] ctx - pldm_msgbuf context for extractor
298 * @param[out] dst - destination of extracted value
299 *
300 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
301 * PLDM_ERROR_INVALID_LENGTH otherwise.
302 * PLDM_ERROR_INVALID_DATA if input a invalid ctx
303 */
Andrew Jeffery66c77232024-04-24 11:42:02 +0930304#define pldm_msgbuf_extract_uint8(ctx, dst) \
305 pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \
306 dst, ctx, dst)
307// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
308static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030309{
310 if (!ctx || !ctx->cursor || !dst) {
311 return PLDM_ERROR_INVALID_DATA;
312 }
313
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930314 if (ctx->remaining == INTMAX_MIN) {
315 assert(ctx->remaining < 0);
316 return PLDM_ERROR_INVALID_LENGTH;
317 }
Andrew Jeffery66c77232024-04-24 11:42:02 +0930318 ctx->remaining -= sizeof(uint8_t);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030319 assert(ctx->remaining >= 0);
320 if (ctx->remaining < 0) {
321 return PLDM_ERROR_INVALID_LENGTH;
322 }
323
Andrew Jeffery66c77232024-04-24 11:42:02 +0930324 memcpy(dst, ctx->cursor, sizeof(uint8_t));
325
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030326 ctx->cursor++;
327 return PLDM_SUCCESS;
328}
329
Andrew Jeffery66c77232024-04-24 11:42:02 +0930330#define pldm_msgbuf_extract_int8(ctx, dst) \
331 pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \
332 ctx, dst)
333// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
334static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030335{
336 if (!ctx || !ctx->cursor || !dst) {
337 return PLDM_ERROR_INVALID_DATA;
338 }
339
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930340 if (ctx->remaining == INTMAX_MIN) {
341 assert(ctx->remaining < 0);
342 return PLDM_ERROR_INVALID_LENGTH;
343 }
Andrew Jeffery66c77232024-04-24 11:42:02 +0930344 ctx->remaining -= sizeof(int8_t);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030345 assert(ctx->remaining >= 0);
346 if (ctx->remaining < 0) {
347 return PLDM_ERROR_INVALID_LENGTH;
348 }
349
Andrew Jeffery66c77232024-04-24 11:42:02 +0930350 memcpy(dst, ctx->cursor, sizeof(int8_t));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030351 ctx->cursor++;
352 return PLDM_SUCCESS;
353}
354
Andrew Jeffery66c77232024-04-24 11:42:02 +0930355#define pldm_msgbuf_extract_uint16(ctx, dst) \
356 pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \
357 dst, ctx, dst)
358// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
359static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
360 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030361{
362 uint16_t ldst;
363
364 if (!ctx || !ctx->cursor || !dst) {
365 return PLDM_ERROR_INVALID_DATA;
366 }
367
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930368 // Check for underflow while tracking the magnitude of the buffer overflow
369 static_assert(
370 // NOLINTNEXTLINE(bugprone-sizeof-expression)
371 sizeof(ldst) < INTMAX_MAX,
372 "The following addition may not uphold the runtime assertion");
373 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
374 assert(ctx->remaining < 0);
375 return PLDM_ERROR_INVALID_LENGTH;
376 }
377
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030378 // Check for buffer overflow. If we overflow, account for the request as
379 // negative values in ctx->remaining. This way we can debug how far
380 // we've overflowed.
381 ctx->remaining -= sizeof(ldst);
382
383 // Prevent the access if it would overflow. First, assert so we blow up
384 // the test suite right at the point of failure. However, cater to
385 // -DNDEBUG by explicitly testing that the access is valid.
386 assert(ctx->remaining >= 0);
387 if (ctx->remaining < 0) {
388 return PLDM_ERROR_INVALID_LENGTH;
389 }
390
391 // Use memcpy() to have the compiler deal with any alignment
392 // issues on the target architecture
393 memcpy(&ldst, ctx->cursor, sizeof(ldst));
394
395 // Only assign the target value once it's correctly decoded
Andrew Jeffery66c77232024-04-24 11:42:02 +0930396 ldst = le16toh(ldst);
397
398 // Allow storing to unaligned
399 memcpy(dst, &ldst, sizeof(ldst));
400
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030401 ctx->cursor += sizeof(ldst);
402
403 return PLDM_SUCCESS;
404}
405
Andrew Jeffery66c77232024-04-24 11:42:02 +0930406#define pldm_msgbuf_extract_int16(ctx, dst) \
407 pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \
408 dst, ctx, dst)
409// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
410static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030411{
412 int16_t ldst;
413
414 if (!ctx || !ctx->cursor || !dst) {
415 return PLDM_ERROR_INVALID_DATA;
416 }
417
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930418 static_assert(
419 // NOLINTNEXTLINE(bugprone-sizeof-expression)
420 sizeof(ldst) < INTMAX_MAX,
421 "The following addition may not uphold the runtime assertion");
422 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
423 assert(ctx->remaining < 0);
424 return PLDM_ERROR_INVALID_LENGTH;
425 }
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030426 ctx->remaining -= sizeof(ldst);
427 assert(ctx->remaining >= 0);
428 if (ctx->remaining < 0) {
429 return PLDM_ERROR_INVALID_LENGTH;
430 }
431
432 memcpy(&ldst, ctx->cursor, sizeof(ldst));
433
Andrew Jeffery66c77232024-04-24 11:42:02 +0930434 ldst = le16toh(ldst);
435 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030436 ctx->cursor += sizeof(ldst);
437
438 return PLDM_SUCCESS;
439}
440
Andrew Jeffery66c77232024-04-24 11:42:02 +0930441#define pldm_msgbuf_extract_uint32(ctx, dst) \
442 pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \
443 dst, ctx, dst)
444// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
445static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
446 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030447{
448 uint32_t ldst;
449
450 if (!ctx || !ctx->cursor || !dst) {
451 return PLDM_ERROR_INVALID_DATA;
452 }
453
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930454 static_assert(
455 // NOLINTNEXTLINE(bugprone-sizeof-expression)
456 sizeof(ldst) < INTMAX_MAX,
457 "The following addition may not uphold the runtime assertion");
458 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
459 assert(ctx->remaining < 0);
460 return PLDM_ERROR_INVALID_LENGTH;
461 }
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030462 ctx->remaining -= sizeof(ldst);
463 assert(ctx->remaining >= 0);
464 if (ctx->remaining < 0) {
465 return PLDM_ERROR_INVALID_LENGTH;
466 }
467
468 memcpy(&ldst, ctx->cursor, sizeof(ldst));
469
Andrew Jeffery66c77232024-04-24 11:42:02 +0930470 ldst = le32toh(ldst);
471 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030472 ctx->cursor += sizeof(ldst);
473
474 return PLDM_SUCCESS;
475}
476
Andrew Jeffery66c77232024-04-24 11:42:02 +0930477#define pldm_msgbuf_extract_int32(ctx, dst) \
478 pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \
479 dst, ctx, dst)
480// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
481static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030482{
483 int32_t ldst;
484
485 if (!ctx || !ctx->cursor || !dst) {
486 return PLDM_ERROR_INVALID_DATA;
487 }
488
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930489 static_assert(
490 // NOLINTNEXTLINE(bugprone-sizeof-expression)
491 sizeof(ldst) < INTMAX_MAX,
492 "The following addition may not uphold the runtime assertion");
493 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
494 assert(ctx->remaining < 0);
495 return PLDM_ERROR_INVALID_LENGTH;
496 }
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030497 ctx->remaining -= sizeof(ldst);
498 assert(ctx->remaining >= 0);
499 if (ctx->remaining < 0) {
500 return PLDM_ERROR_INVALID_LENGTH;
501 }
502
503 memcpy(&ldst, ctx->cursor, sizeof(ldst));
504
Andrew Jeffery66c77232024-04-24 11:42:02 +0930505 ldst = le32toh(ldst);
506 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030507 ctx->cursor += sizeof(ldst);
508
509 return PLDM_SUCCESS;
510}
511
Andrew Jeffery66c77232024-04-24 11:42:02 +0930512#define pldm_msgbuf_extract_real32(ctx, dst) \
513 pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \
514 dst, ctx, dst)
515// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
516static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx,
517 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030518{
519 uint32_t ldst;
520
Andrew Jeffery66c77232024-04-24 11:42:02 +0930521 _Static_assert(sizeof(real32_t) == sizeof(ldst),
522 "Mismatched type sizes for dst and ldst");
523
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030524 if (!ctx || !ctx->cursor || !dst) {
525 return PLDM_ERROR_INVALID_DATA;
526 }
527
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930528 static_assert(
529 // NOLINTNEXTLINE(bugprone-sizeof-expression)
530 sizeof(ldst) < INTMAX_MAX,
531 "The following addition may not uphold the runtime assertion");
532 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(ldst)) {
533 assert(ctx->remaining < 0);
534 return PLDM_ERROR_INVALID_LENGTH;
535 }
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030536 ctx->remaining -= sizeof(ldst);
537 assert(ctx->remaining >= 0);
538 if (ctx->remaining < 0) {
539 return PLDM_ERROR_INVALID_LENGTH;
540 }
541
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030542 memcpy(&ldst, ctx->cursor, sizeof(ldst));
543 ldst = le32toh(ldst);
Andrew Jeffery66c77232024-04-24 11:42:02 +0930544 memcpy(dst, &ldst, sizeof(ldst));
545 ctx->cursor += sizeof(ldst);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030546
547 return PLDM_SUCCESS;
548}
549
Andrew Jeffery66c77232024-04-24 11:42:02 +0930550/**
551 * Extract the field at the msgbuf cursor into the lvalue named by dst.
552 *
553 * @param ctx The msgbuf context object
554 * @param dst The lvalue into which the field at the msgbuf cursor should be
555 * extracted
556 *
557 * @return PLDM_SUCCESS on success, otherwise another value on error
558 */
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030559#define pldm_msgbuf_extract(ctx, dst) \
Andrew Jeffery66c77232024-04-24 11:42:02 +0930560 _Generic((dst), \
561 uint8_t: pldm__msgbuf_extract_uint8, \
562 int8_t: pldm__msgbuf_extract_int8, \
563 uint16_t: pldm__msgbuf_extract_uint16, \
564 int16_t: pldm__msgbuf_extract_int16, \
565 uint32_t: pldm__msgbuf_extract_uint32, \
566 int32_t: pldm__msgbuf_extract_int32, \
567 real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
568
569/**
570 * Extract the field at the msgbuf cursor into the object pointed-to by dst.
571 *
572 * @param ctx The msgbuf context object
573 * @param dst The pointer to the object into which the field at the msgbuf
574 * cursor should be extracted
575 *
576 * @return PLDM_SUCCESS on success, otherwise another value on error
577 */
578#define pldm_msgbuf_extract_p(ctx, dst) \
579 _Generic((dst), \
580 uint8_t *: pldm__msgbuf_extract_uint8, \
581 int8_t *: pldm__msgbuf_extract_int8, \
582 uint16_t *: pldm__msgbuf_extract_uint16, \
583 int16_t *: pldm__msgbuf_extract_int16, \
584 uint32_t *: pldm__msgbuf_extract_uint32, \
585 int32_t *: pldm__msgbuf_extract_int32, \
586 real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030587
Andrew Jeffery369b1212023-04-20 15:44:48 +0930588static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
589 uint8_t *dst, size_t count)
590{
Andrew Jeffery369b1212023-04-20 15:44:48 +0930591 if (!ctx || !ctx->cursor || !dst) {
592 return PLDM_ERROR_INVALID_DATA;
593 }
594
595 if (!count) {
596 return PLDM_SUCCESS;
597 }
598
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930599#if INTMAX_MAX < SIZE_MAX
600 if (count > INTMAX_MAX) {
Andrew Jeffery369b1212023-04-20 15:44:48 +0930601 return PLDM_ERROR_INVALID_LENGTH;
602 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930603#endif
Andrew Jeffery369b1212023-04-20 15:44:48 +0930604
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930605 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
606 return PLDM_ERROR_INVALID_LENGTH;
607 }
608 ctx->remaining -= (intmax_t)count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930609 assert(ctx->remaining >= 0);
610 if (ctx->remaining < 0) {
611 return PLDM_ERROR_INVALID_LENGTH;
612 }
613
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030614 memcpy(dst, ctx->cursor, count);
615 ctx->cursor += count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930616
617 return PLDM_SUCCESS;
618}
619
620#define pldm_msgbuf_extract_array(ctx, dst, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930621 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
622 count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930623
Thu Nguyen062c8762023-04-22 20:45:04 +0700624static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
625 const uint32_t src)
626{
627 uint32_t val = htole32(src);
628
629 if (!ctx || !ctx->cursor) {
630 return PLDM_ERROR_INVALID_DATA;
631 }
632
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930633 static_assert(
634 // NOLINTNEXTLINE(bugprone-sizeof-expression)
635 sizeof(src) < INTMAX_MAX,
636 "The following addition may not uphold the runtime assertion");
637 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
638 assert(ctx->remaining < 0);
639 return PLDM_ERROR_INVALID_LENGTH;
640 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700641 ctx->remaining -= sizeof(src);
642 assert(ctx->remaining >= 0);
643 if (ctx->remaining < 0) {
644 return PLDM_ERROR_INVALID_LENGTH;
645 }
646
647 memcpy(ctx->cursor, &val, sizeof(val));
648 ctx->cursor += sizeof(src);
649
650 return PLDM_SUCCESS;
651}
652
653static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
654 const uint16_t src)
655{
656 uint16_t val = htole16(src);
657
658 if (!ctx || !ctx->cursor) {
659 return PLDM_ERROR_INVALID_DATA;
660 }
661
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930662 static_assert(
663 // NOLINTNEXTLINE(bugprone-sizeof-expression)
664 sizeof(src) < INTMAX_MAX,
665 "The following addition may not uphold the runtime assertion");
666 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
667 assert(ctx->remaining < 0);
668 return PLDM_ERROR_INVALID_LENGTH;
669 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700670 ctx->remaining -= sizeof(src);
671 assert(ctx->remaining >= 0);
672 if (ctx->remaining < 0) {
673 return PLDM_ERROR_INVALID_LENGTH;
674 }
675
676 memcpy(ctx->cursor, &val, sizeof(val));
677 ctx->cursor += sizeof(src);
678
679 return PLDM_SUCCESS;
680}
681
682static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
683 const uint8_t src)
684{
685 if (!ctx || !ctx->cursor) {
686 return PLDM_ERROR_INVALID_DATA;
687 }
688
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930689 static_assert(
690 // NOLINTNEXTLINE(bugprone-sizeof-expression)
691 sizeof(src) < INTMAX_MAX,
692 "The following addition may not uphold the runtime assertion");
693 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
694 assert(ctx->remaining < 0);
695 return PLDM_ERROR_INVALID_LENGTH;
696 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700697 ctx->remaining -= sizeof(src);
698 assert(ctx->remaining >= 0);
699 if (ctx->remaining < 0) {
700 return PLDM_ERROR_INVALID_LENGTH;
701 }
702
703 memcpy(ctx->cursor, &src, sizeof(src));
704 ctx->cursor += sizeof(src);
705
706 return PLDM_SUCCESS;
707}
708
709static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
710 const int32_t src)
711{
712 int32_t val = htole32(src);
713
714 if (!ctx || !ctx->cursor) {
715 return PLDM_ERROR_INVALID_DATA;
716 }
717
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930718 static_assert(
719 // NOLINTNEXTLINE(bugprone-sizeof-expression)
720 sizeof(src) < INTMAX_MAX,
721 "The following addition may not uphold the runtime assertion");
722 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
723 assert(ctx->remaining < 0);
724 return PLDM_ERROR_INVALID_LENGTH;
725 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700726 ctx->remaining -= sizeof(src);
727 assert(ctx->remaining >= 0);
728 if (ctx->remaining < 0) {
729 return PLDM_ERROR_INVALID_LENGTH;
730 }
731
732 memcpy(ctx->cursor, &val, sizeof(val));
733 ctx->cursor += sizeof(src);
734
735 return PLDM_SUCCESS;
736}
737
738static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
739 const int16_t src)
740{
741 int16_t val = htole16(src);
742
743 if (!ctx || !ctx->cursor) {
744 return PLDM_ERROR_INVALID_DATA;
745 }
746
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930747 static_assert(
748 // NOLINTNEXTLINE(bugprone-sizeof-expression)
749 sizeof(src) < INTMAX_MAX,
750 "The following addition may not uphold the runtime assertion");
751 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
752 assert(ctx->remaining < 0);
753 return PLDM_ERROR_INVALID_LENGTH;
754 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700755 ctx->remaining -= sizeof(src);
756 assert(ctx->remaining >= 0);
757 if (ctx->remaining < 0) {
758 return PLDM_ERROR_INVALID_LENGTH;
759 }
760
761 memcpy(ctx->cursor, &val, sizeof(val));
762 ctx->cursor += sizeof(src);
763
764 return PLDM_SUCCESS;
765}
766
767static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
768 const int8_t src)
769{
770 if (!ctx || !ctx->cursor) {
771 return PLDM_ERROR_INVALID_DATA;
772 }
773
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930774 static_assert(
775 // NOLINTNEXTLINE(bugprone-sizeof-expression)
776 sizeof(src) < INTMAX_MAX,
777 "The following addition may not uphold the runtime assertion");
778 if (ctx->remaining < INTMAX_MIN + (intmax_t)sizeof(src)) {
779 assert(ctx->remaining < 0);
780 return PLDM_ERROR_INVALID_LENGTH;
781 }
Thu Nguyen062c8762023-04-22 20:45:04 +0700782 ctx->remaining -= sizeof(src);
783 assert(ctx->remaining >= 0);
784 if (ctx->remaining < 0) {
785 return PLDM_ERROR_INVALID_LENGTH;
786 }
787
788 memcpy(ctx->cursor, &src, sizeof(src));
789 ctx->cursor += sizeof(src);
790
791 return PLDM_SUCCESS;
792}
793
794#define pldm_msgbuf_insert(dst, src) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930795 _Generic((src), \
796 uint8_t: pldm_msgbuf_insert_uint8, \
797 int8_t: pldm_msgbuf_insert_int8, \
798 uint16_t: pldm_msgbuf_insert_uint16, \
799 int16_t: pldm_msgbuf_insert_int16, \
800 uint32_t: pldm_msgbuf_insert_uint32, \
801 int32_t: pldm_msgbuf_insert_int32)(dst, src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700802
803static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
804 const uint8_t *src,
805 size_t count)
806{
Thu Nguyen062c8762023-04-22 20:45:04 +0700807 if (!ctx || !ctx->cursor || !src) {
808 return PLDM_ERROR_INVALID_DATA;
809 }
810
811 if (!count) {
812 return PLDM_SUCCESS;
813 }
814
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930815#if INTMAX_MAX < SIZE_MAX
816 if (count > INTMAX_MAX) {
Thu Nguyen062c8762023-04-22 20:45:04 +0700817 return PLDM_ERROR_INVALID_LENGTH;
818 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930819#endif
Thu Nguyen062c8762023-04-22 20:45:04 +0700820
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930821 if (ctx->remaining < INTMAX_MIN + (intmax_t)count) {
822 return PLDM_ERROR_INVALID_LENGTH;
823 }
824 ctx->remaining -= (intmax_t)count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700825 assert(ctx->remaining >= 0);
826 if (ctx->remaining < 0) {
827 return PLDM_ERROR_INVALID_LENGTH;
828 }
829
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030830 memcpy(ctx->cursor, src, count);
831 ctx->cursor += count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700832
833 return PLDM_SUCCESS;
834}
835
836#define pldm_msgbuf_insert_array(dst, src, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930837 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \
838 count)
Thu Nguyen062c8762023-04-22 20:45:04 +0700839
840static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
841 size_t required, void **cursor)
842{
843 if (!ctx || !ctx->cursor || !cursor || *cursor) {
844 return PLDM_ERROR_INVALID_DATA;
845 }
846
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930847#if INTMAX_MAX < SIZE_MAX
848 if (required > INTMAX_MAX) {
Thu Nguyen062c8762023-04-22 20:45:04 +0700849 return PLDM_ERROR_INVALID_LENGTH;
850 }
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930851#endif
Thu Nguyen062c8762023-04-22 20:45:04 +0700852
Andrew Jeffery2ff8cf82024-05-17 15:20:46 +0930853 if (ctx->remaining < INTMAX_MIN + (intmax_t)required) {
854 return PLDM_ERROR_INVALID_LENGTH;
855 }
856 ctx->remaining -= (intmax_t)required;
Thu Nguyen062c8762023-04-22 20:45:04 +0700857 assert(ctx->remaining >= 0);
858 if (ctx->remaining < 0) {
859 return PLDM_ERROR_INVALID_LENGTH;
860 }
861
862 *cursor = ctx->cursor;
863 ctx->cursor += required;
864
865 return PLDM_SUCCESS;
866}
867
868static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
869 void **cursor, size_t *len)
870{
871 if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
872 return PLDM_ERROR_INVALID_DATA;
873 }
874
875 assert(ctx->remaining >= 0);
876 if (ctx->remaining < 0) {
877 return PLDM_ERROR_INVALID_LENGTH;
878 }
879
880 *cursor = ctx->cursor;
881 ctx->cursor += ctx->remaining;
882 *len = ctx->remaining;
883 ctx->remaining = 0;
884
885 return PLDM_SUCCESS;
886}
Varsha Kaverappa909bf7c2024-05-03 06:18:42 -0500887
888/**
889 * @brief pldm_msgbuf copy data between two msg buffers
890 *
891 * @param[inout] src - pldm_msgbuf for source from where value should be copied
892 * @param[inout] dst - destination of copy from source
893 * @param[in] size - size of data to be copied
894 * @param[in] description - description of data copied
895 *
896 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
897 * PLDM_ERROR_INVALID_LENGTH otherwise.
898 * PLDM_ERROR_INVALID_DATA if input is invalid
899 */
900#define pldm_msgbuf_copy(dst, src, type, name) \
901 pldm__msgbuf_copy(dst, src, sizeof(type), #name)
902// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
903static inline int pldm__msgbuf_copy(struct pldm_msgbuf *dst,
904 struct pldm_msgbuf *src, size_t size,
905 const char *description)
906{
907 if (!src || !src->cursor || !dst || !dst->cursor || !description) {
908 return PLDM_ERROR_INVALID_DATA;
909 }
910
911#if INTMAX_MAX < SIZE_MAX
912 if (size > INTMAX_MAX) {
913 return PLDM_ERROR_INVALID_LENGTH;
914 }
915#endif
916
917 if (src->remaining < INTMAX_MIN + (intmax_t)size) {
918 return PLDM_ERROR_INVALID_LENGTH;
919 }
920
921 if (dst->remaining < INTMAX_MIN + (intmax_t)size) {
922 return PLDM_ERROR_INVALID_LENGTH;
923 }
924
925 src->remaining -= (intmax_t)size;
926 assert(src->remaining >= 0);
927 if (src->remaining < 0) {
928 return PLDM_ERROR_INVALID_LENGTH;
929 }
930
931 dst->remaining -= (intmax_t)size;
932 assert(dst->remaining >= 0);
933 if (dst->remaining < 0) {
934 return PLDM_ERROR_INVALID_LENGTH;
935 }
936
937 memcpy(dst->cursor, src->cursor, size);
938 src->cursor += size;
939 dst->cursor += size;
940
941 return PLDM_SUCCESS;
942}
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030943#ifdef __cplusplus
944}
945#endif
946
Andrew Jeffery66c77232024-04-24 11:42:02 +0930947#ifdef __cplusplus
948#include <type_traits>
949
950template <typename T>
951static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
952 void *buf)
953{
954 static_assert(std::is_same<uint8_t *, T>::value);
955 return pldm__msgbuf_extract_uint8(ctx, buf);
956}
957
958template <typename T>
959static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
960 void *buf)
961{
962 static_assert(std::is_same<int8_t *, T>::value);
963 return pldm__msgbuf_extract_int8(ctx, buf);
964}
965
966template <typename T>
967static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
968 void *buf)
969{
970 static_assert(std::is_same<uint16_t *, T>::value);
971 return pldm__msgbuf_extract_uint16(ctx, buf);
972}
973
974template <typename T>
975static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
976 void *buf)
977{
978 static_assert(std::is_same<int16_t *, T>::value);
979 return pldm__msgbuf_extract_int16(ctx, buf);
980}
981
982template <typename T>
983static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
984 void *buf)
985{
986 static_assert(std::is_same<uint32_t *, T>::value);
987 return pldm__msgbuf_extract_uint32(ctx, buf);
988}
989
990template <typename T>
991static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
992 void *buf)
993{
994 static_assert(std::is_same<int32_t *, T>::value);
995 return pldm__msgbuf_extract_int32(ctx, buf);
996}
997
998template <typename T>
999static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
1000 void *buf)
1001{
1002 static_assert(std::is_same<real32_t *, T>::value);
1003 return pldm__msgbuf_extract_real32(ctx, buf);
1004}
1005#endif
1006
Andrew Jefferyc63f63a2023-02-24 22:29:33 +10301007#endif /* BUF_H */