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 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 */ 91 static 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 /** 101 * @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 */ 110 static 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 /** 127 * @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 */ 137 static 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 /** 154 * @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 */ 163 static 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 181 static 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 198 static 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 231 static 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 254 static 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 277 static 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 300 static 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 335 static 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 369 #ifdef __cplusplus 370 } 371 #endif 372 373 #endif /* BUF_H */ 374