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