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