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