xref: /openbmc/libpldm/src/msgbuf.h (revision c63f63a2)
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 static 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  */
70 static 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  */
89 static 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  */
115 static 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 
133 static 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 
150 static 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 
183 static 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 
206 static 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 
229 static 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 
252 static 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 */
292