blob: b32daf00d2f2f03185f2d961d579218a7ec0622a [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{
Andrew Jeffery369b1212023-04-20 15:44:48 +0930346 if (!ctx || !ctx->cursor || !dst) {
347 return PLDM_ERROR_INVALID_DATA;
348 }
349
350 if (!count) {
351 return PLDM_SUCCESS;
352 }
353
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030354 if (count >= SSIZE_MAX) {
Andrew Jeffery369b1212023-04-20 15:44:48 +0930355 return PLDM_ERROR_INVALID_LENGTH;
356 }
357
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030358 ctx->remaining -= (ssize_t)count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930359 assert(ctx->remaining >= 0);
360 if (ctx->remaining < 0) {
361 return PLDM_ERROR_INVALID_LENGTH;
362 }
363
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030364 memcpy(dst, ctx->cursor, count);
365 ctx->cursor += count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930366
367 return PLDM_SUCCESS;
368}
369
370#define pldm_msgbuf_extract_array(ctx, dst, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930371 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
372 count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930373
Thu Nguyen062c8762023-04-22 20:45:04 +0700374static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
375 const uint32_t src)
376{
377 uint32_t val = htole32(src);
378
379 if (!ctx || !ctx->cursor) {
380 return PLDM_ERROR_INVALID_DATA;
381 }
382
383 ctx->remaining -= sizeof(src);
384 assert(ctx->remaining >= 0);
385 if (ctx->remaining < 0) {
386 return PLDM_ERROR_INVALID_LENGTH;
387 }
388
389 memcpy(ctx->cursor, &val, sizeof(val));
390 ctx->cursor += sizeof(src);
391
392 return PLDM_SUCCESS;
393}
394
395static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
396 const uint16_t src)
397{
398 uint16_t val = htole16(src);
399
400 if (!ctx || !ctx->cursor) {
401 return PLDM_ERROR_INVALID_DATA;
402 }
403
404 ctx->remaining -= sizeof(src);
405 assert(ctx->remaining >= 0);
406 if (ctx->remaining < 0) {
407 return PLDM_ERROR_INVALID_LENGTH;
408 }
409
410 memcpy(ctx->cursor, &val, sizeof(val));
411 ctx->cursor += sizeof(src);
412
413 return PLDM_SUCCESS;
414}
415
416static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
417 const uint8_t src)
418{
419 if (!ctx || !ctx->cursor) {
420 return PLDM_ERROR_INVALID_DATA;
421 }
422
423 ctx->remaining -= sizeof(src);
424 assert(ctx->remaining >= 0);
425 if (ctx->remaining < 0) {
426 return PLDM_ERROR_INVALID_LENGTH;
427 }
428
429 memcpy(ctx->cursor, &src, sizeof(src));
430 ctx->cursor += sizeof(src);
431
432 return PLDM_SUCCESS;
433}
434
435static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
436 const int32_t src)
437{
438 int32_t val = htole32(src);
439
440 if (!ctx || !ctx->cursor) {
441 return PLDM_ERROR_INVALID_DATA;
442 }
443
444 ctx->remaining -= sizeof(src);
445 assert(ctx->remaining >= 0);
446 if (ctx->remaining < 0) {
447 return PLDM_ERROR_INVALID_LENGTH;
448 }
449
450 memcpy(ctx->cursor, &val, sizeof(val));
451 ctx->cursor += sizeof(src);
452
453 return PLDM_SUCCESS;
454}
455
456static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
457 const int16_t src)
458{
459 int16_t val = htole16(src);
460
461 if (!ctx || !ctx->cursor) {
462 return PLDM_ERROR_INVALID_DATA;
463 }
464
465 ctx->remaining -= sizeof(src);
466 assert(ctx->remaining >= 0);
467 if (ctx->remaining < 0) {
468 return PLDM_ERROR_INVALID_LENGTH;
469 }
470
471 memcpy(ctx->cursor, &val, sizeof(val));
472 ctx->cursor += sizeof(src);
473
474 return PLDM_SUCCESS;
475}
476
477static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
478 const int8_t src)
479{
480 if (!ctx || !ctx->cursor) {
481 return PLDM_ERROR_INVALID_DATA;
482 }
483
484 ctx->remaining -= sizeof(src);
485 assert(ctx->remaining >= 0);
486 if (ctx->remaining < 0) {
487 return PLDM_ERROR_INVALID_LENGTH;
488 }
489
490 memcpy(ctx->cursor, &src, sizeof(src));
491 ctx->cursor += sizeof(src);
492
493 return PLDM_SUCCESS;
494}
495
496#define pldm_msgbuf_insert(dst, src) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930497 _Generic((src), \
498 uint8_t: pldm_msgbuf_insert_uint8, \
499 int8_t: pldm_msgbuf_insert_int8, \
500 uint16_t: pldm_msgbuf_insert_uint16, \
501 int16_t: pldm_msgbuf_insert_int16, \
502 uint32_t: pldm_msgbuf_insert_uint32, \
503 int32_t: pldm_msgbuf_insert_int32)(dst, src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700504
505static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
506 const uint8_t *src,
507 size_t count)
508{
Thu Nguyen062c8762023-04-22 20:45:04 +0700509 if (!ctx || !ctx->cursor || !src) {
510 return PLDM_ERROR_INVALID_DATA;
511 }
512
513 if (!count) {
514 return PLDM_SUCCESS;
515 }
516
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030517 if (count >= SSIZE_MAX) {
Thu Nguyen062c8762023-04-22 20:45:04 +0700518 return PLDM_ERROR_INVALID_LENGTH;
519 }
520
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030521 ctx->remaining -= (ssize_t)count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700522 assert(ctx->remaining >= 0);
523 if (ctx->remaining < 0) {
524 return PLDM_ERROR_INVALID_LENGTH;
525 }
526
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030527 memcpy(ctx->cursor, src, count);
528 ctx->cursor += count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700529
530 return PLDM_SUCCESS;
531}
532
533#define pldm_msgbuf_insert_array(dst, src, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930534 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \
535 count)
Thu Nguyen062c8762023-04-22 20:45:04 +0700536
537static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
538 size_t required, void **cursor)
539{
540 if (!ctx || !ctx->cursor || !cursor || *cursor) {
541 return PLDM_ERROR_INVALID_DATA;
542 }
543
544 if (required > SSIZE_MAX) {
545 return PLDM_ERROR_INVALID_LENGTH;
546 }
547
548 ctx->remaining -= (ssize_t)required;
549 assert(ctx->remaining >= 0);
550 if (ctx->remaining < 0) {
551 return PLDM_ERROR_INVALID_LENGTH;
552 }
553
554 *cursor = ctx->cursor;
555 ctx->cursor += required;
556
557 return PLDM_SUCCESS;
558}
559
560static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
561 void **cursor, size_t *len)
562{
563 if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
564 return PLDM_ERROR_INVALID_DATA;
565 }
566
567 assert(ctx->remaining >= 0);
568 if (ctx->remaining < 0) {
569 return PLDM_ERROR_INVALID_LENGTH;
570 }
571
572 *cursor = ctx->cursor;
573 ctx->cursor += ctx->remaining;
574 *len = ctx->remaining;
575 ctx->remaining = 0;
576
577 return PLDM_SUCCESS;
578}
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030579#ifdef __cplusplus
580}
581#endif
582
583#endif /* BUF_H */