xref: /openbmc/libpldm/src/msgbuf.h (revision 5646f23b)
1 #ifndef PLDM_MSGBUF_H
2 #define PLDM_MSGBUF_H
3 
4 #ifdef __cplusplus
5 extern "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 
18 struct 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  */
35 __attribute__((no_sanitize("pointer-overflow"))) static inline int
36 pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
37 		 size_t len)
38 {
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  */
71 static 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  */
90 static 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  */
116 static 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 
134 static 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 
151 static 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 
184 static 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 
207 static 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 
230 static 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 
253 static 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 */
293