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