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