blob: 548959047fdd6212f18bd1d10711c42baebca93b [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 */
35static inline int pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize,
36 const void *buf, size_t len)
37{
38 uint8_t *end;
39
40 if (!ctx || !buf) {
41 return PLDM_ERROR_INVALID_DATA;
42 }
43
44 if ((minsize > len) || (len > SSIZE_MAX)) {
45 return PLDM_ERROR_INVALID_LENGTH;
46 }
47
48 end = (uint8_t *)buf + len;
49 if (end && end < (uint8_t *)buf) {
50 return PLDM_ERROR_INVALID_LENGTH;
51 }
52
53 ctx->cursor = (uint8_t *)buf;
54 ctx->remaining = (ssize_t)len;
55
56 return PLDM_SUCCESS;
57}
58
59/**
60 * @brief Validate buffer overflow state
61 *
62 * @param[in] ctx - pldm_msgbuf context for extractor
63 *
64 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
65 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
66 * prior accesses would have occurred beyond the bounds of the buffer, and
67 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
68 * pointer.
69 */
70static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
71{
72 if (!ctx) {
73 return PLDM_ERROR_INVALID_DATA;
74 }
75
76 return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
77}
78
79/**
80 * @brief Destroy the pldm buf
81 *
82 * @param[in] ctx - pldm_msgbuf context for extractor
83 *
84 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
85 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
86 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
87 * bounds of the buffer.
88 */
89static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
90{
91 int valid;
92
93 if (!ctx) {
94 return PLDM_ERROR_INVALID_DATA;
95 }
96
97 valid = pldm_msgbuf_validate(ctx);
98
99 ctx->cursor = NULL;
100 ctx->remaining = 0;
101
102 return valid;
103}
104
105/**
106 * @brief pldm_msgbuf extractor for a uint8_t
107 *
108 * @param[inout] ctx - pldm_msgbuf context for extractor
109 * @param[out] dst - destination of extracted value
110 *
111 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
112 * PLDM_ERROR_INVALID_LENGTH otherwise.
113 * PLDM_ERROR_INVALID_DATA if input a invalid ctx
114 */
115static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx,
116 uint8_t *dst)
117{
118 if (!ctx || !ctx->cursor || !dst) {
119 return PLDM_ERROR_INVALID_DATA;
120 }
121
122 ctx->remaining -= sizeof(*dst);
123 assert(ctx->remaining >= 0);
124 if (ctx->remaining < 0) {
125 return PLDM_ERROR_INVALID_LENGTH;
126 }
127
128 *dst = *((uint8_t *)(ctx->cursor));
129 ctx->cursor++;
130 return PLDM_SUCCESS;
131}
132
133static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_t *dst)
134{
135 if (!ctx || !ctx->cursor || !dst) {
136 return PLDM_ERROR_INVALID_DATA;
137 }
138
139 ctx->remaining -= sizeof(*dst);
140 assert(ctx->remaining >= 0);
141 if (ctx->remaining < 0) {
142 return PLDM_ERROR_INVALID_LENGTH;
143 }
144
145 *dst = *((int8_t *)(ctx->cursor));
146 ctx->cursor++;
147 return PLDM_SUCCESS;
148}
149
150static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
151 uint16_t *dst)
152{
153 uint16_t ldst;
154
155 if (!ctx || !ctx->cursor || !dst) {
156 return PLDM_ERROR_INVALID_DATA;
157 }
158
159 // Check for buffer overflow. If we overflow, account for the request as
160 // negative values in ctx->remaining. This way we can debug how far
161 // we've overflowed.
162 ctx->remaining -= sizeof(ldst);
163
164 // Prevent the access if it would overflow. First, assert so we blow up
165 // the test suite right at the point of failure. However, cater to
166 // -DNDEBUG by explicitly testing that the access is valid.
167 assert(ctx->remaining >= 0);
168 if (ctx->remaining < 0) {
169 return PLDM_ERROR_INVALID_LENGTH;
170 }
171
172 // Use memcpy() to have the compiler deal with any alignment
173 // issues on the target architecture
174 memcpy(&ldst, ctx->cursor, sizeof(ldst));
175
176 // Only assign the target value once it's correctly decoded
177 *dst = le16toh(ldst);
178 ctx->cursor += sizeof(ldst);
179
180 return PLDM_SUCCESS;
181}
182
183static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx,
184 int16_t *dst)
185{
186 int16_t ldst;
187
188 if (!ctx || !ctx->cursor || !dst) {
189 return PLDM_ERROR_INVALID_DATA;
190 }
191
192 ctx->remaining -= sizeof(ldst);
193 assert(ctx->remaining >= 0);
194 if (ctx->remaining < 0) {
195 return PLDM_ERROR_INVALID_LENGTH;
196 }
197
198 memcpy(&ldst, ctx->cursor, sizeof(ldst));
199
200 *dst = le16toh(ldst);
201 ctx->cursor += sizeof(ldst);
202
203 return PLDM_SUCCESS;
204}
205
206static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
207 uint32_t *dst)
208{
209 uint32_t ldst;
210
211 if (!ctx || !ctx->cursor || !dst) {
212 return PLDM_ERROR_INVALID_DATA;
213 }
214
215 ctx->remaining -= sizeof(ldst);
216 assert(ctx->remaining >= 0);
217 if (ctx->remaining < 0) {
218 return PLDM_ERROR_INVALID_LENGTH;
219 }
220
221 memcpy(&ldst, ctx->cursor, sizeof(ldst));
222
223 *dst = le32toh(ldst);
224 ctx->cursor += sizeof(ldst);
225
226 return PLDM_SUCCESS;
227}
228
229static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx,
230 int32_t *dst)
231{
232 int32_t ldst;
233
234 if (!ctx || !ctx->cursor || !dst) {
235 return PLDM_ERROR_INVALID_DATA;
236 }
237
238 ctx->remaining -= sizeof(ldst);
239 assert(ctx->remaining >= 0);
240 if (ctx->remaining < 0) {
241 return PLDM_ERROR_INVALID_LENGTH;
242 }
243
244 memcpy(&ldst, ctx->cursor, sizeof(ldst));
245
246 *dst = le32toh(ldst);
247 ctx->cursor += sizeof(ldst);
248
249 return PLDM_SUCCESS;
250}
251
252static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx,
253 real32_t *dst)
254{
255 uint32_t ldst;
256
257 if (!ctx || !ctx->cursor || !dst) {
258 return PLDM_ERROR_INVALID_DATA;
259 }
260
261 ctx->remaining -= sizeof(ldst);
262 assert(ctx->remaining >= 0);
263 if (ctx->remaining < 0) {
264 return PLDM_ERROR_INVALID_LENGTH;
265 }
266
267 _Static_assert(sizeof(*dst) == sizeof(ldst),
268 "Mismatched type sizes for dst and ldst");
269 memcpy(&ldst, ctx->cursor, sizeof(ldst));
270 ldst = le32toh(ldst);
271 memcpy(dst, &ldst, sizeof(*dst));
272 ctx->cursor += sizeof(*dst);
273
274 return PLDM_SUCCESS;
275}
276
277#define pldm_msgbuf_extract(ctx, dst) \
278 _Generic((*(dst)), uint8_t \
279 : pldm_msgbuf_extract_uint8, int8_t \
280 : pldm_msgbuf_extract_int8, uint16_t \
281 : pldm_msgbuf_extract_uint16, int16_t \
282 : pldm_msgbuf_extract_int16, uint32_t \
283 : pldm_msgbuf_extract_uint32, int32_t \
284 : pldm_msgbuf_extract_int32, real32_t \
285 : pldm_msgbuf_extract_real32)(ctx, dst)
286
287#ifdef __cplusplus
288}
289#endif
290
291#endif /* BUF_H */