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