1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 3 /* Fuzzing should always have assertions */ 4 #ifdef NDEBUG 5 #undef NDEBUG 6 #endif 7 8 #include <libpldm/base.h> 9 #include <libpldm/firmware_fd.h> 10 #include <libpldm/firmware_update.h> 11 #include <libpldm/sizes.h> 12 13 #include <cstdarg> 14 #include <cstdint> 15 #include <cstring> 16 #include <memory> 17 #include <vector> 18 19 #include "array.h" 20 #include "msgbuf.h" 21 22 /* Avoid out-of-memory, and 23 * avoid wasting time on inputs larger than MCTP message limits */ 24 static const uint32_t MAX_PART = 200; 25 26 /* Maximum "send" buffer. Should be larger than any expected sent message */ 27 static const uint32_t MAX_SEND = 1024; 28 29 /* Arbitrary EID */ 30 static const uint8_t FIXED_ADDR = 20; 31 32 static const uint8_t PROGRESS_PERCENT = 5; 33 34 static const ssize_t FUZZCTRL_SIZE = 0x400; 35 36 static bool printf_enabled; 37 // NOLINTNEXTLINE(cert-dcl50-cpp) 38 static void debug_printf(const char* fmt, ...) 39 { 40 if (printf_enabled) 41 { 42 va_list ap; 43 va_start(ap, fmt); 44 vprintf(fmt, ap); 45 va_end(ap); 46 } 47 } 48 49 struct fuzz_ops_ctx 50 { 51 struct pldm_msgbuf* fuzz_ctrl; 52 53 /* Details of in-progress update, for consistency checking */ 54 bool current_update; 55 struct pldm_firmware_update_component update_comp; 56 uint32_t offset; 57 bool transferred; 58 bool verified; 59 bool applied; 60 61 uint64_t now; 62 }; 63 64 /* Returns true with roughly `percent` chance */ 65 static bool fuzz_chance(struct fuzz_ops_ctx* ctx, uint8_t percent) 66 { 67 uint8_t v; 68 assert(percent <= 100); 69 int rc = pldm_msgbuf_extract_uint8(ctx->fuzz_ctrl, v); 70 if (rc != 0) 71 { 72 return false; 73 } 74 uint8_t cutoff = (uint32_t)percent * UINT8_MAX / 100; 75 return v <= cutoff; 76 } 77 78 // openbmc 49871 79 static const uint8_t openbmc_iana[] = { 80 0xcf, 81 0xc2, 82 0x00, 83 0x00, 84 }; 85 86 /* An arbitrary but valid set of descriptors. 87 * Short to be readily discoverable by fuzzing */ 88 static const pldm_descriptor FIXED_DESCRIPTORS[] = { 89 { 90 .descriptor_type = PLDM_FWUP_IANA_ENTERPRISE_ID, 91 .descriptor_length = 4, 92 .descriptor_data = openbmc_iana, 93 }, 94 }; 95 96 static int cb_device_identifiers(void* ctx LIBPLDM_CC_UNUSED, 97 uint8_t* descriptors_count, 98 const struct pldm_descriptor** descriptors) 99 { 100 debug_printf("cb_device_identifiers\n"); 101 *descriptors_count = 1; 102 *descriptors = FIXED_DESCRIPTORS; 103 return 0; 104 } 105 106 static const struct pldm_firmware_component_standalone comp = { 107 .comp_classification = PLDM_COMP_UNKNOWN, 108 .comp_identifier = 0, 109 .comp_classification_index = 0, 110 .active_ver = 111 { 112 .comparison_stamp = 1, 113 .str = 114 { 115 .str_type = PLDM_STR_TYPE_UTF_8, 116 .str_len = 3, 117 .str_data = "zzz", 118 }, 119 .date = {0}, 120 }, 121 .pending_ver = 122 { 123 .comparison_stamp = 1, 124 .str = 125 { 126 .str_type = PLDM_STR_TYPE_UNKNOWN, 127 .str_len = 4, 128 .str_data = "fnnn", 129 }, 130 .date = {0}, 131 }, 132 .comp_activation_methods = {0}, 133 .capabilities_during_update = {0}, 134 }; 135 136 static const struct pldm_firmware_component_standalone* comp_list[1] = { 137 &comp, 138 }; 139 140 static int cb_components( 141 void* ctx, uint16_t* ret_entry_count, 142 const struct pldm_firmware_component_standalone*** ret_entries) 143 { 144 debug_printf("cb_components\n"); 145 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 146 147 *ret_entry_count = ARRAY_SIZE(comp_list); 148 *ret_entries = comp_list; 149 if (fuzz_chance(fuzz_ctx, 4)) 150 { 151 return -EINVAL; 152 } 153 return 0; 154 } 155 156 static int cb_imageset_versions(void* ctx, struct pldm_firmware_string* active, 157 struct pldm_firmware_string* pending) 158 { 159 debug_printf("cb_imageset_versions\n"); 160 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 161 162 active->str_type = PLDM_STR_TYPE_ASCII; 163 active->str_len = 4; 164 memcpy(active->str_data, "1234", 4); 165 pending->str_type = PLDM_STR_TYPE_ASCII; 166 pending->str_len = 4; 167 memcpy(pending->str_data, "1235", 4); 168 if (fuzz_chance(fuzz_ctx, 4)) 169 { 170 return -EINVAL; 171 } 172 return 0; 173 } 174 175 static enum pldm_component_response_codes 176 cb_update_component(void* ctx, bool update, 177 const struct pldm_firmware_update_component* comp) 178 { 179 debug_printf("cb_update_component update=%d\n", update); 180 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 181 182 if (fuzz_chance(fuzz_ctx, 4)) 183 { 184 return PLDM_CRC_COMP_PREREQUISITES_NOT_MET; 185 } 186 if (update) 187 { 188 /* Set up a new update */ 189 assert(!fuzz_ctx->current_update); 190 debug_printf("cb_update_component set current_update=true\n"); 191 fuzz_ctx->current_update = true; 192 fuzz_ctx->transferred = false; 193 fuzz_ctx->verified = false; 194 fuzz_ctx->applied = false; 195 fuzz_ctx->offset = 0; 196 memcpy(&fuzz_ctx->update_comp, comp, sizeof(*comp)); 197 } 198 return PLDM_CRC_COMP_CAN_BE_UPDATED; 199 } 200 201 static uint32_t cb_transfer_size(void* ctx, uint32_t ua_max_transfer_size) 202 { 203 debug_printf("cb_transfer_size ua_size=%zu\n", 204 (ssize_t)ua_max_transfer_size); 205 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 206 207 if (fuzz_chance(fuzz_ctx, 50)) 208 { 209 // Sometimes adjust it 210 return MAX_PART - 20; 211 } 212 return ua_max_transfer_size; 213 } 214 215 static uint8_t 216 cb_firmware_data(void* ctx, uint32_t offset, 217 const uint8_t* data LIBPLDM_CC_UNUSED, uint32_t len, 218 const struct pldm_firmware_update_component* comp) 219 { 220 debug_printf("cb_firmware_data offset=%zu len %zu\n", (size_t)offset, 221 (size_t)len); 222 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 223 224 assert(fuzz_ctx->current_update); 225 assert(!fuzz_ctx->transferred); 226 assert(!fuzz_ctx->verified); 227 assert(!fuzz_ctx->applied); 228 assert(offset == fuzz_ctx->offset); 229 fuzz_ctx->offset += len; 230 assert(fuzz_ctx->offset <= fuzz_ctx->update_comp.comp_image_size); 231 assert(memcmp(comp, &fuzz_ctx->update_comp, sizeof(*comp)) == 0); 232 233 if (fuzz_ctx->offset == fuzz_ctx->update_comp.comp_image_size) 234 { 235 fuzz_ctx->transferred = true; 236 } 237 238 if (fuzz_chance(fuzz_ctx, 2)) 239 { 240 return PLDM_FWUP_TRANSFER_ERROR_IMAGE_CORRUPT; 241 } 242 return PLDM_FWUP_TRANSFER_SUCCESS; 243 } 244 245 static uint8_t cb_verify(void* ctx, 246 const struct pldm_firmware_update_component* comp, 247 bool* ret_pending, 248 uint8_t* ret_percent_complete LIBPLDM_CC_UNUSED) 249 { 250 debug_printf("cb_verify\n"); 251 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 252 253 assert(fuzz_ctx->current_update); 254 assert(fuzz_ctx->transferred); 255 assert(!fuzz_ctx->verified); 256 assert(!fuzz_ctx->applied); 257 assert(memcmp(comp, &fuzz_ctx->update_comp, sizeof(*comp)) == 0); 258 259 if (fuzz_chance(fuzz_ctx, 5)) 260 { 261 debug_printf("cb_verify set failure\n"); 262 return PLDM_FWUP_VERIFY_ERROR_VERSION_MISMATCH; 263 } 264 265 if (fuzz_chance(fuzz_ctx, 50)) 266 { 267 debug_printf("cb_verify set ret_pending=true\n"); 268 *ret_pending = true; 269 } 270 else 271 { 272 fuzz_ctx->verified = true; 273 } 274 275 return PLDM_SUCCESS; 276 } 277 278 static uint8_t cb_apply(void* ctx, 279 const struct pldm_firmware_update_component* comp, 280 bool* ret_pending, 281 uint8_t* ret_percent_complete LIBPLDM_CC_UNUSED) 282 { 283 debug_printf("cb_apply\n"); 284 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 285 286 assert(fuzz_ctx->current_update); 287 assert(fuzz_ctx->transferred); 288 assert(fuzz_ctx->verified); 289 assert(!fuzz_ctx->applied); 290 assert(memcmp(comp, &fuzz_ctx->update_comp, sizeof(*comp)) == 0); 291 292 if (fuzz_chance(fuzz_ctx, 5)) 293 { 294 debug_printf("cb_apply set failure\n"); 295 return PLDM_FWUP_APPLY_FAILURE_MEMORY_ISSUE; 296 } 297 298 if (fuzz_chance(fuzz_ctx, 50)) 299 { 300 debug_printf("cb_apply set ret_pending=true\n"); 301 *ret_pending = true; 302 } 303 else 304 { 305 debug_printf("cb_apply set current_update=false\n"); 306 fuzz_ctx->current_update = false; 307 fuzz_ctx->applied = true; 308 } 309 310 return PLDM_SUCCESS; 311 } 312 313 static uint8_t cb_activate(void* ctx, bool self_contained LIBPLDM_CC_UNUSED, 314 uint16_t* ret_estimated_time LIBPLDM_CC_UNUSED) 315 { 316 debug_printf("cb_activate\n"); 317 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 318 319 assert(!fuzz_ctx->current_update); 320 if (fuzz_chance(fuzz_ctx, 5)) 321 { 322 return PLDM_ERROR; 323 } 324 return PLDM_SUCCESS; 325 } 326 327 static void cb_cancel_update_component( 328 void* ctx, const struct pldm_firmware_update_component* comp) 329 { 330 debug_printf("cb_cancel_update_component\n"); 331 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 332 333 assert(fuzz_ctx->current_update); 334 assert(fuzz_ctx->offset <= fuzz_ctx->update_comp.comp_image_size); 335 assert(memcmp(comp, &fuzz_ctx->update_comp, sizeof(*comp)) == 0); 336 fuzz_ctx->current_update = false; 337 } 338 339 static uint64_t cb_now(void* ctx) 340 { 341 struct fuzz_ops_ctx* fuzz_ctx = static_cast<struct fuzz_ops_ctx*>(ctx); 342 343 // Arbitrary 3s increment. FD code has a 1s retry timeout. 344 fuzz_ctx->now += 3000; 345 return fuzz_ctx->now; 346 } 347 348 static const struct pldm_fd_ops fuzz_ops = { 349 .device_identifiers = cb_device_identifiers, 350 .components = cb_components, 351 .imageset_versions = cb_imageset_versions, 352 .update_component = cb_update_component, 353 .transfer_size = cb_transfer_size, 354 .firmware_data = cb_firmware_data, 355 .verify = cb_verify, 356 .apply = cb_apply, 357 .activate = cb_activate, 358 .cancel_update_component = cb_cancel_update_component, 359 .now = cb_now, 360 }; 361 362 extern "C" int LLVMFuzzerInitialize(int* argc LIBPLDM_CC_UNUSED, 363 char*** argv LIBPLDM_CC_UNUSED) 364 { 365 printf_enabled = getenv("TRACEFWFD"); 366 return 0; 367 } 368 369 extern "C" int LLVMFuzzerTestOneInput(uint8_t* input, size_t len) 370 { 371 PLDM_MSGBUF_DEFINE_P(fuzzproto); 372 PLDM_MSGBUF_DEFINE_P(fuzzctrl); 373 int rc; 374 375 /* Split input into two parts. First FUZZCTRL_SIZE (0x400 bytes currently) 376 * is used for fuzzing control (random choices etc). 377 * The remainder is a PLDM packet stream, of length:data */ 378 if (len < FUZZCTRL_SIZE) 379 { 380 return 0; 381 } 382 size_t proto_size = len - FUZZCTRL_SIZE; 383 rc = pldm_msgbuf_init_errno(fuzzctrl, 0, input, FUZZCTRL_SIZE); 384 assert(rc == 0); 385 rc = 386 pldm_msgbuf_init_errno(fuzzproto, 0, &input[FUZZCTRL_SIZE], proto_size); 387 assert(rc == 0); 388 389 auto ops_ctx = std::make_unique<fuzz_ops_ctx>(); 390 memset(ops_ctx.get(), 0x0, sizeof(fuzz_ops_ctx)); 391 /* callbacks may consume bytes from the fuzz control */ 392 ops_ctx->fuzz_ctrl = fuzzctrl; 393 394 struct pldm_fd* fd = pldm_fd_new(&fuzz_ops, ops_ctx.get(), NULL); 395 assert(fd); 396 397 while (true) 398 { 399 /* Arbitrary length send buffer */ 400 uint32_t send_len; 401 rc = pldm_msgbuf_extract_uint32(fuzzctrl, send_len); 402 if (rc) 403 { 404 break; 405 } 406 send_len %= (MAX_SEND + 1); 407 std::vector<uint8_t> send_buf(send_len); 408 409 size_t len = send_buf.size(); 410 /* Either perform pldm_fd_handle_msg() or pldm_fd_progress() */ 411 if (fuzz_chance(ops_ctx.get(), PROGRESS_PERCENT)) 412 { 413 uint8_t address = FIXED_ADDR; 414 pldm_fd_progress(fd, send_buf.data(), &len, &address); 415 } 416 else 417 { 418 uint32_t part_len; 419 rc = pldm_msgbuf_extract_uint32(fuzzproto, part_len); 420 if (rc) 421 { 422 break; 423 } 424 part_len = std::min(part_len, MAX_PART); 425 /* Fresh allocation so that ASAN will notice overflow reads */ 426 std::vector<uint8_t> part_buf(part_len); 427 rc = pldm_msgbuf_extract_array_uint8( 428 fuzzproto, part_len, part_buf.data(), part_buf.size()); 429 if (rc != 0) 430 { 431 break; 432 } 433 pldm_fd_handle_msg(fd, FIXED_ADDR, part_buf.data(), part_buf.size(), 434 send_buf.data(), &len); 435 } 436 assert(len <= send_buf.size()); 437 } 438 rc = pldm_msgbuf_discard(fuzzproto, rc); 439 rc = pldm_msgbuf_discard(fuzzctrl, rc); 440 (void)rc; 441 442 free(fd); 443 return 0; 444 } 445 446 #ifdef HFND_FUZZING_ENTRY_FUNCTION 447 #define USING_HONGGFUZZ 1 448 #else 449 #define USING_HONGGFUZZ 0 450 #endif 451 452 #ifdef __AFL_FUZZ_TESTCASE_LEN 453 #define USING_AFL 1 454 #else 455 #define USING_AFL 0 456 #endif 457 458 #if USING_AFL 459 __AFL_FUZZ_INIT(); 460 #endif 461 462 #if !USING_AFL && !USING_HONGGFUZZ 463 /* Let it build without AFL taking stdin instead */ 464 static void run_standalone() 465 { 466 while (true) 467 { 468 unsigned char buf[1024000]; 469 ssize_t len = read(STDIN_FILENO, buf, sizeof(buf)); 470 if (len <= 0) 471 { 472 break; 473 } 474 LLVMFuzzerTestOneInput(buf, len); 475 } 476 } 477 #endif 478 479 #if !USING_HONGGFUZZ 480 int main(int argc, char** argv) 481 { 482 LLVMFuzzerInitialize(&argc, &argv); 483 484 #if USING_AFL 485 __AFL_INIT(); 486 uint8_t* buf = __AFL_FUZZ_TESTCASE_BUF; 487 488 while (__AFL_LOOP(100000)) 489 { 490 size_t len = __AFL_FUZZ_TESTCASE_LEN; 491 LLVMFuzzerTestOneInput(buf, len); 492 } 493 #else 494 run_standalone(); 495 #endif 496 497 return 0; 498 } 499 #endif // !USING_HONGGFUZZ 500