blob: 662ba4626d06d8338868672dddad464dd049062d [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
18struct pldm_msgbuf {
19 const uint8_t *cursor;
20 ssize_t remaining;
21};
22
23/**
24 * @brief Initialize pldm buf struct for buf extractor
25 *
26 * @param[out] ctx - pldm_msgbuf context for extractor
27 * @param[in] minsize - The minimum required length of buffer `buf`
28 * @param[in] buf - buffer to be extracted
29 * @param[in] len - size of buffer
30 *
31 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
32 * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
33 * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
34 */
Andrew Jeffery5646f232023-04-12 22:40:19 +093035__attribute__((no_sanitize("pointer-overflow"))) static inline int
36pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
37 size_t len)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103038{
39 uint8_t *end;
40
41 if (!ctx || !buf) {
42 return PLDM_ERROR_INVALID_DATA;
43 }
44
45 if ((minsize > len) || (len > SSIZE_MAX)) {
46 return PLDM_ERROR_INVALID_LENGTH;
47 }
48
49 end = (uint8_t *)buf + len;
50 if (end && end < (uint8_t *)buf) {
51 return PLDM_ERROR_INVALID_LENGTH;
52 }
53
54 ctx->cursor = (uint8_t *)buf;
55 ctx->remaining = (ssize_t)len;
56
57 return PLDM_SUCCESS;
58}
59
60/**
61 * @brief Validate buffer overflow state
62 *
63 * @param[in] ctx - pldm_msgbuf context for extractor
64 *
65 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
66 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
67 * prior accesses would have occurred beyond the bounds of the buffer, and
68 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
69 * pointer.
70 */
71static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
72{
73 if (!ctx) {
74 return PLDM_ERROR_INVALID_DATA;
75 }
76
77 return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
78}
79
80/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +093081 * @brief Test whether a message buffer has been exactly consumed
82 *
83 * @param[in] ctx - pldm_msgbuf context for extractor
84 *
85 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
86 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
87 * indicates that an incorrect sequence of accesses have occurred, and
88 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
89 * pointer.
90 */
91static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
92{
93 if (!ctx) {
94 return PLDM_ERROR_INVALID_DATA;
95 }
96
97 return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
98}
99
100/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030101 * @brief Destroy the pldm buf
102 *
103 * @param[in] ctx - pldm_msgbuf context for extractor
104 *
105 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
106 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
107 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
108 * bounds of the buffer.
109 */
110static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
111{
112 int valid;
113
114 if (!ctx) {
115 return PLDM_ERROR_INVALID_DATA;
116 }
117
118 valid = pldm_msgbuf_validate(ctx);
119
120 ctx->cursor = NULL;
121 ctx->remaining = 0;
122
123 return valid;
124}
125
126/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930127 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
128 * has been completely consumed without overflow
129 *
130 * @param[in] ctx - pldm_msgbuf context
131 *
132 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
133 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
134 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
135 * have occurred byond the bounds of the buffer
136 */
137static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
138{
139 int consumed;
140
141 if (!ctx) {
142 return PLDM_ERROR_INVALID_DATA;
143 }
144
145 consumed = pldm_msgbuf_consumed(ctx);
146
147 ctx->cursor = NULL;
148 ctx->remaining = 0;
149
150 return consumed;
151}
152
153/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030154 * @brief pldm_msgbuf extractor for a uint8_t
155 *
156 * @param[inout] ctx - pldm_msgbuf context for extractor
157 * @param[out] dst - destination of extracted value
158 *
159 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
160 * PLDM_ERROR_INVALID_LENGTH otherwise.
161 * PLDM_ERROR_INVALID_DATA if input a invalid ctx
162 */
163static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx,
164 uint8_t *dst)
165{
166 if (!ctx || !ctx->cursor || !dst) {
167 return PLDM_ERROR_INVALID_DATA;
168 }
169
170 ctx->remaining -= sizeof(*dst);
171 assert(ctx->remaining >= 0);
172 if (ctx->remaining < 0) {
173 return PLDM_ERROR_INVALID_LENGTH;
174 }
175
176 *dst = *((uint8_t *)(ctx->cursor));
177 ctx->cursor++;
178 return PLDM_SUCCESS;
179}
180
181static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_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 = *((int8_t *)(ctx->cursor));
194 ctx->cursor++;
195 return PLDM_SUCCESS;
196}
197
198static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
199 uint16_t *dst)
200{
201 uint16_t ldst;
202
203 if (!ctx || !ctx->cursor || !dst) {
204 return PLDM_ERROR_INVALID_DATA;
205 }
206
207 // Check for buffer overflow. If we overflow, account for the request as
208 // negative values in ctx->remaining. This way we can debug how far
209 // we've overflowed.
210 ctx->remaining -= sizeof(ldst);
211
212 // Prevent the access if it would overflow. First, assert so we blow up
213 // the test suite right at the point of failure. However, cater to
214 // -DNDEBUG by explicitly testing that the access is valid.
215 assert(ctx->remaining >= 0);
216 if (ctx->remaining < 0) {
217 return PLDM_ERROR_INVALID_LENGTH;
218 }
219
220 // Use memcpy() to have the compiler deal with any alignment
221 // issues on the target architecture
222 memcpy(&ldst, ctx->cursor, sizeof(ldst));
223
224 // Only assign the target value once it's correctly decoded
225 *dst = le16toh(ldst);
226 ctx->cursor += sizeof(ldst);
227
228 return PLDM_SUCCESS;
229}
230
231static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx,
232 int16_t *dst)
233{
234 int16_t ldst;
235
236 if (!ctx || !ctx->cursor || !dst) {
237 return PLDM_ERROR_INVALID_DATA;
238 }
239
240 ctx->remaining -= sizeof(ldst);
241 assert(ctx->remaining >= 0);
242 if (ctx->remaining < 0) {
243 return PLDM_ERROR_INVALID_LENGTH;
244 }
245
246 memcpy(&ldst, ctx->cursor, sizeof(ldst));
247
248 *dst = le16toh(ldst);
249 ctx->cursor += sizeof(ldst);
250
251 return PLDM_SUCCESS;
252}
253
254static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
255 uint32_t *dst)
256{
257 uint32_t ldst;
258
259 if (!ctx || !ctx->cursor || !dst) {
260 return PLDM_ERROR_INVALID_DATA;
261 }
262
263 ctx->remaining -= sizeof(ldst);
264 assert(ctx->remaining >= 0);
265 if (ctx->remaining < 0) {
266 return PLDM_ERROR_INVALID_LENGTH;
267 }
268
269 memcpy(&ldst, ctx->cursor, sizeof(ldst));
270
271 *dst = le32toh(ldst);
272 ctx->cursor += sizeof(ldst);
273
274 return PLDM_SUCCESS;
275}
276
277static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx,
278 int32_t *dst)
279{
280 int32_t ldst;
281
282 if (!ctx || !ctx->cursor || !dst) {
283 return PLDM_ERROR_INVALID_DATA;
284 }
285
286 ctx->remaining -= sizeof(ldst);
287 assert(ctx->remaining >= 0);
288 if (ctx->remaining < 0) {
289 return PLDM_ERROR_INVALID_LENGTH;
290 }
291
292 memcpy(&ldst, ctx->cursor, sizeof(ldst));
293
294 *dst = le32toh(ldst);
295 ctx->cursor += sizeof(ldst);
296
297 return PLDM_SUCCESS;
298}
299
300static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx,
301 real32_t *dst)
302{
303 uint32_t ldst;
304
305 if (!ctx || !ctx->cursor || !dst) {
306 return PLDM_ERROR_INVALID_DATA;
307 }
308
309 ctx->remaining -= sizeof(ldst);
310 assert(ctx->remaining >= 0);
311 if (ctx->remaining < 0) {
312 return PLDM_ERROR_INVALID_LENGTH;
313 }
314
315 _Static_assert(sizeof(*dst) == sizeof(ldst),
316 "Mismatched type sizes for dst and ldst");
317 memcpy(&ldst, ctx->cursor, sizeof(ldst));
318 ldst = le32toh(ldst);
319 memcpy(dst, &ldst, sizeof(*dst));
320 ctx->cursor += sizeof(*dst);
321
322 return PLDM_SUCCESS;
323}
324
325#define pldm_msgbuf_extract(ctx, dst) \
326 _Generic((*(dst)), uint8_t \
327 : pldm_msgbuf_extract_uint8, int8_t \
328 : pldm_msgbuf_extract_int8, uint16_t \
329 : pldm_msgbuf_extract_uint16, int16_t \
330 : pldm_msgbuf_extract_int16, uint32_t \
331 : pldm_msgbuf_extract_uint32, int32_t \
332 : pldm_msgbuf_extract_int32, real32_t \
333 : pldm_msgbuf_extract_real32)(ctx, dst)
334
Andrew Jeffery369b1212023-04-20 15:44:48 +0930335static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
336 uint8_t *dst, size_t count)
337{
338 size_t len;
339
340 if (!ctx || !ctx->cursor || !dst) {
341 return PLDM_ERROR_INVALID_DATA;
342 }
343
344 if (!count) {
345 return PLDM_SUCCESS;
346 }
347
348 len = sizeof(*dst) * count;
349 if (len > SSIZE_MAX) {
350 return PLDM_ERROR_INVALID_LENGTH;
351 }
352
353 ctx->remaining -= (ssize_t)len;
354 assert(ctx->remaining >= 0);
355 if (ctx->remaining < 0) {
356 return PLDM_ERROR_INVALID_LENGTH;
357 }
358
359 memcpy(dst, ctx->cursor, len);
360 ctx->cursor += len;
361
362 return PLDM_SUCCESS;
363}
364
365#define pldm_msgbuf_extract_array(ctx, dst, count) \
366 _Generic((*(dst)), uint8_t \
367 : pldm_msgbuf_extract_array_uint8)(ctx, dst, count)
368
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030369#ifdef __cplusplus
370}
371#endif
372
373#endif /* BUF_H */