blob: c9527e178a66ad5d8caa4961809680065feb8261 [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
5#ifdef __cplusplus
Andrew Jeffery37dd6a32023-05-12 16:04:06 +09306/*
7 * Fix up C11's _Static_assert() vs C++'s static_assert().
8 *
9 * Can we please have nice things for once.
10 */
11// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
12#define _Static_assert(...) static_assert(__VA_ARGS__)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103013extern "C" {
14#endif
15
Andrew Jefferyb0c1d202023-11-07 22:08:44 +103016#include <libpldm/base.h>
17#include <libpldm/pldm_types.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103018
19#include <assert.h>
20#include <endian.h>
21#include <limits.h>
22#include <stdbool.h>
23#include <string.h>
24#include <sys/types.h>
25
26struct pldm_msgbuf {
Thu Nguyen062c8762023-04-22 20:45:04 +070027 uint8_t *cursor;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103028 ssize_t remaining;
29};
30
31/**
32 * @brief Initialize pldm buf struct for buf extractor
33 *
34 * @param[out] ctx - pldm_msgbuf context for extractor
35 * @param[in] minsize - The minimum required length of buffer `buf`
36 * @param[in] buf - buffer to be extracted
37 * @param[in] len - size of buffer
38 *
39 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
40 * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
41 * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
42 */
Andrew Jeffery5646f232023-04-12 22:40:19 +093043__attribute__((no_sanitize("pointer-overflow"))) static inline int
44pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
45 size_t len)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103046{
47 uint8_t *end;
48
49 if (!ctx || !buf) {
50 return PLDM_ERROR_INVALID_DATA;
51 }
52
53 if ((minsize > len) || (len > SSIZE_MAX)) {
54 return PLDM_ERROR_INVALID_LENGTH;
55 }
56
57 end = (uint8_t *)buf + len;
58 if (end && end < (uint8_t *)buf) {
59 return PLDM_ERROR_INVALID_LENGTH;
60 }
61
62 ctx->cursor = (uint8_t *)buf;
63 ctx->remaining = (ssize_t)len;
64
65 return PLDM_SUCCESS;
66}
67
68/**
69 * @brief Validate buffer overflow state
70 *
71 * @param[in] ctx - pldm_msgbuf context for extractor
72 *
73 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
74 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
75 * prior accesses would have occurred beyond the bounds of the buffer, and
76 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
77 * pointer.
78 */
79static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
80{
81 if (!ctx) {
82 return PLDM_ERROR_INVALID_DATA;
83 }
84
85 return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
86}
87
88/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +093089 * @brief Test whether a message buffer has been exactly consumed
90 *
91 * @param[in] ctx - pldm_msgbuf context for extractor
92 *
93 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
94 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
95 * indicates that an incorrect sequence of accesses have occurred, and
96 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
97 * pointer.
98 */
99static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
100{
101 if (!ctx) {
102 return PLDM_ERROR_INVALID_DATA;
103 }
104
105 return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
106}
107
108/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030109 * @brief Destroy the pldm buf
110 *
111 * @param[in] ctx - pldm_msgbuf context for extractor
112 *
113 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
114 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
115 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
116 * bounds of the buffer.
117 */
118static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
119{
120 int valid;
121
122 if (!ctx) {
123 return PLDM_ERROR_INVALID_DATA;
124 }
125
126 valid = pldm_msgbuf_validate(ctx);
127
128 ctx->cursor = NULL;
129 ctx->remaining = 0;
130
131 return valid;
132}
133
134/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930135 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
136 * has been completely consumed without overflow
137 *
138 * @param[in] ctx - pldm_msgbuf context
139 *
140 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
141 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
142 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
143 * have occurred byond the bounds of the buffer
144 */
145static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
146{
147 int consumed;
148
149 if (!ctx) {
150 return PLDM_ERROR_INVALID_DATA;
151 }
152
153 consumed = pldm_msgbuf_consumed(ctx);
154
155 ctx->cursor = NULL;
156 ctx->remaining = 0;
157
158 return consumed;
159}
160
161/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030162 * @brief pldm_msgbuf extractor for a uint8_t
163 *
164 * @param[inout] ctx - pldm_msgbuf context for extractor
165 * @param[out] dst - destination of extracted value
166 *
167 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
168 * PLDM_ERROR_INVALID_LENGTH otherwise.
169 * PLDM_ERROR_INVALID_DATA if input a invalid ctx
170 */
171static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx,
172 uint8_t *dst)
173{
174 if (!ctx || !ctx->cursor || !dst) {
175 return PLDM_ERROR_INVALID_DATA;
176 }
177
178 ctx->remaining -= sizeof(*dst);
179 assert(ctx->remaining >= 0);
180 if (ctx->remaining < 0) {
181 return PLDM_ERROR_INVALID_LENGTH;
182 }
183
184 *dst = *((uint8_t *)(ctx->cursor));
185 ctx->cursor++;
186 return PLDM_SUCCESS;
187}
188
189static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_t *dst)
190{
191 if (!ctx || !ctx->cursor || !dst) {
192 return PLDM_ERROR_INVALID_DATA;
193 }
194
195 ctx->remaining -= sizeof(*dst);
196 assert(ctx->remaining >= 0);
197 if (ctx->remaining < 0) {
198 return PLDM_ERROR_INVALID_LENGTH;
199 }
200
201 *dst = *((int8_t *)(ctx->cursor));
202 ctx->cursor++;
203 return PLDM_SUCCESS;
204}
205
206static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
207 uint16_t *dst)
208{
209 uint16_t ldst;
210
211 if (!ctx || !ctx->cursor || !dst) {
212 return PLDM_ERROR_INVALID_DATA;
213 }
214
215 // Check for buffer overflow. If we overflow, account for the request as
216 // negative values in ctx->remaining. This way we can debug how far
217 // we've overflowed.
218 ctx->remaining -= sizeof(ldst);
219
220 // Prevent the access if it would overflow. First, assert so we blow up
221 // the test suite right at the point of failure. However, cater to
222 // -DNDEBUG by explicitly testing that the access is valid.
223 assert(ctx->remaining >= 0);
224 if (ctx->remaining < 0) {
225 return PLDM_ERROR_INVALID_LENGTH;
226 }
227
228 // Use memcpy() to have the compiler deal with any alignment
229 // issues on the target architecture
230 memcpy(&ldst, ctx->cursor, sizeof(ldst));
231
232 // Only assign the target value once it's correctly decoded
233 *dst = le16toh(ldst);
234 ctx->cursor += sizeof(ldst);
235
236 return PLDM_SUCCESS;
237}
238
239static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx,
240 int16_t *dst)
241{
242 int16_t ldst;
243
244 if (!ctx || !ctx->cursor || !dst) {
245 return PLDM_ERROR_INVALID_DATA;
246 }
247
248 ctx->remaining -= sizeof(ldst);
249 assert(ctx->remaining >= 0);
250 if (ctx->remaining < 0) {
251 return PLDM_ERROR_INVALID_LENGTH;
252 }
253
254 memcpy(&ldst, ctx->cursor, sizeof(ldst));
255
256 *dst = le16toh(ldst);
257 ctx->cursor += sizeof(ldst);
258
259 return PLDM_SUCCESS;
260}
261
262static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
263 uint32_t *dst)
264{
265 uint32_t ldst;
266
267 if (!ctx || !ctx->cursor || !dst) {
268 return PLDM_ERROR_INVALID_DATA;
269 }
270
271 ctx->remaining -= sizeof(ldst);
272 assert(ctx->remaining >= 0);
273 if (ctx->remaining < 0) {
274 return PLDM_ERROR_INVALID_LENGTH;
275 }
276
277 memcpy(&ldst, ctx->cursor, sizeof(ldst));
278
279 *dst = le32toh(ldst);
280 ctx->cursor += sizeof(ldst);
281
282 return PLDM_SUCCESS;
283}
284
285static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx,
286 int32_t *dst)
287{
288 int32_t ldst;
289
290 if (!ctx || !ctx->cursor || !dst) {
291 return PLDM_ERROR_INVALID_DATA;
292 }
293
294 ctx->remaining -= sizeof(ldst);
295 assert(ctx->remaining >= 0);
296 if (ctx->remaining < 0) {
297 return PLDM_ERROR_INVALID_LENGTH;
298 }
299
300 memcpy(&ldst, ctx->cursor, sizeof(ldst));
301
302 *dst = le32toh(ldst);
303 ctx->cursor += sizeof(ldst);
304
305 return PLDM_SUCCESS;
306}
307
308static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx,
309 real32_t *dst)
310{
311 uint32_t ldst;
312
313 if (!ctx || !ctx->cursor || !dst) {
314 return PLDM_ERROR_INVALID_DATA;
315 }
316
317 ctx->remaining -= sizeof(ldst);
318 assert(ctx->remaining >= 0);
319 if (ctx->remaining < 0) {
320 return PLDM_ERROR_INVALID_LENGTH;
321 }
322
323 _Static_assert(sizeof(*dst) == sizeof(ldst),
324 "Mismatched type sizes for dst and ldst");
325 memcpy(&ldst, ctx->cursor, sizeof(ldst));
326 ldst = le32toh(ldst);
327 memcpy(dst, &ldst, sizeof(*dst));
328 ctx->cursor += sizeof(*dst);
329
330 return PLDM_SUCCESS;
331}
332
333#define pldm_msgbuf_extract(ctx, dst) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930334 _Generic((*(dst)), \
335 uint8_t: pldm_msgbuf_extract_uint8, \
336 int8_t: pldm_msgbuf_extract_int8, \
337 uint16_t: pldm_msgbuf_extract_uint16, \
338 int16_t: pldm_msgbuf_extract_int16, \
339 uint32_t: pldm_msgbuf_extract_uint32, \
340 int32_t: pldm_msgbuf_extract_int32, \
341 real32_t: pldm_msgbuf_extract_real32)(ctx, dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030342
Andrew Jeffery369b1212023-04-20 15:44:48 +0930343static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
344 uint8_t *dst, size_t count)
345{
346 size_t len;
347
348 if (!ctx || !ctx->cursor || !dst) {
349 return PLDM_ERROR_INVALID_DATA;
350 }
351
352 if (!count) {
353 return PLDM_SUCCESS;
354 }
355
356 len = sizeof(*dst) * count;
357 if (len > SSIZE_MAX) {
358 return PLDM_ERROR_INVALID_LENGTH;
359 }
360
361 ctx->remaining -= (ssize_t)len;
362 assert(ctx->remaining >= 0);
363 if (ctx->remaining < 0) {
364 return PLDM_ERROR_INVALID_LENGTH;
365 }
366
367 memcpy(dst, ctx->cursor, len);
368 ctx->cursor += len;
369
370 return PLDM_SUCCESS;
371}
372
373#define pldm_msgbuf_extract_array(ctx, dst, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930374 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
375 count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930376
Thu Nguyen062c8762023-04-22 20:45:04 +0700377static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
378 const uint32_t src)
379{
380 uint32_t val = htole32(src);
381
382 if (!ctx || !ctx->cursor) {
383 return PLDM_ERROR_INVALID_DATA;
384 }
385
386 ctx->remaining -= sizeof(src);
387 assert(ctx->remaining >= 0);
388 if (ctx->remaining < 0) {
389 return PLDM_ERROR_INVALID_LENGTH;
390 }
391
392 memcpy(ctx->cursor, &val, sizeof(val));
393 ctx->cursor += sizeof(src);
394
395 return PLDM_SUCCESS;
396}
397
398static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
399 const uint16_t src)
400{
401 uint16_t val = htole16(src);
402
403 if (!ctx || !ctx->cursor) {
404 return PLDM_ERROR_INVALID_DATA;
405 }
406
407 ctx->remaining -= sizeof(src);
408 assert(ctx->remaining >= 0);
409 if (ctx->remaining < 0) {
410 return PLDM_ERROR_INVALID_LENGTH;
411 }
412
413 memcpy(ctx->cursor, &val, sizeof(val));
414 ctx->cursor += sizeof(src);
415
416 return PLDM_SUCCESS;
417}
418
419static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
420 const uint8_t src)
421{
422 if (!ctx || !ctx->cursor) {
423 return PLDM_ERROR_INVALID_DATA;
424 }
425
426 ctx->remaining -= sizeof(src);
427 assert(ctx->remaining >= 0);
428 if (ctx->remaining < 0) {
429 return PLDM_ERROR_INVALID_LENGTH;
430 }
431
432 memcpy(ctx->cursor, &src, sizeof(src));
433 ctx->cursor += sizeof(src);
434
435 return PLDM_SUCCESS;
436}
437
438static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
439 const int32_t src)
440{
441 int32_t val = htole32(src);
442
443 if (!ctx || !ctx->cursor) {
444 return PLDM_ERROR_INVALID_DATA;
445 }
446
447 ctx->remaining -= sizeof(src);
448 assert(ctx->remaining >= 0);
449 if (ctx->remaining < 0) {
450 return PLDM_ERROR_INVALID_LENGTH;
451 }
452
453 memcpy(ctx->cursor, &val, sizeof(val));
454 ctx->cursor += sizeof(src);
455
456 return PLDM_SUCCESS;
457}
458
459static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
460 const int16_t src)
461{
462 int16_t val = htole16(src);
463
464 if (!ctx || !ctx->cursor) {
465 return PLDM_ERROR_INVALID_DATA;
466 }
467
468 ctx->remaining -= sizeof(src);
469 assert(ctx->remaining >= 0);
470 if (ctx->remaining < 0) {
471 return PLDM_ERROR_INVALID_LENGTH;
472 }
473
474 memcpy(ctx->cursor, &val, sizeof(val));
475 ctx->cursor += sizeof(src);
476
477 return PLDM_SUCCESS;
478}
479
480static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
481 const int8_t src)
482{
483 if (!ctx || !ctx->cursor) {
484 return PLDM_ERROR_INVALID_DATA;
485 }
486
487 ctx->remaining -= sizeof(src);
488 assert(ctx->remaining >= 0);
489 if (ctx->remaining < 0) {
490 return PLDM_ERROR_INVALID_LENGTH;
491 }
492
493 memcpy(ctx->cursor, &src, sizeof(src));
494 ctx->cursor += sizeof(src);
495
496 return PLDM_SUCCESS;
497}
498
499#define pldm_msgbuf_insert(dst, src) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930500 _Generic((src), \
501 uint8_t: pldm_msgbuf_insert_uint8, \
502 int8_t: pldm_msgbuf_insert_int8, \
503 uint16_t: pldm_msgbuf_insert_uint16, \
504 int16_t: pldm_msgbuf_insert_int16, \
505 uint32_t: pldm_msgbuf_insert_uint32, \
506 int32_t: pldm_msgbuf_insert_int32)(dst, src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700507
508static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
509 const uint8_t *src,
510 size_t count)
511{
512 size_t len;
513 if (!ctx || !ctx->cursor || !src) {
514 return PLDM_ERROR_INVALID_DATA;
515 }
516
517 if (!count) {
518 return PLDM_SUCCESS;
519 }
520
521 len = sizeof(*src) * count;
522 if (len > SSIZE_MAX) {
523 return PLDM_ERROR_INVALID_LENGTH;
524 }
525
526 ctx->remaining -= (ssize_t)len;
527 assert(ctx->remaining >= 0);
528 if (ctx->remaining < 0) {
529 return PLDM_ERROR_INVALID_LENGTH;
530 }
531
532 memcpy(ctx->cursor, src, len);
533 ctx->cursor += len;
534
535 return PLDM_SUCCESS;
536}
537
538#define pldm_msgbuf_insert_array(dst, src, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930539 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \
540 count)
Thu Nguyen062c8762023-04-22 20:45:04 +0700541
542static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
543 size_t required, void **cursor)
544{
545 if (!ctx || !ctx->cursor || !cursor || *cursor) {
546 return PLDM_ERROR_INVALID_DATA;
547 }
548
549 if (required > SSIZE_MAX) {
550 return PLDM_ERROR_INVALID_LENGTH;
551 }
552
553 ctx->remaining -= (ssize_t)required;
554 assert(ctx->remaining >= 0);
555 if (ctx->remaining < 0) {
556 return PLDM_ERROR_INVALID_LENGTH;
557 }
558
559 *cursor = ctx->cursor;
560 ctx->cursor += required;
561
562 return PLDM_SUCCESS;
563}
564
565static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
566 void **cursor, size_t *len)
567{
568 if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
569 return PLDM_ERROR_INVALID_DATA;
570 }
571
572 assert(ctx->remaining >= 0);
573 if (ctx->remaining < 0) {
574 return PLDM_ERROR_INVALID_LENGTH;
575 }
576
577 *cursor = ctx->cursor;
578 ctx->cursor += ctx->remaining;
579 *len = ctx->remaining;
580 ctx->remaining = 0;
581
582 return PLDM_SUCCESS;
583}
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030584#ifdef __cplusplus
585}
586#endif
587
588#endif /* BUF_H */