1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2022 Intel Corporation. All rights reserved. 3 */ 4 #include "spdk/stdinc.h" 5 #include "spdk/conf.h" 6 #include "spdk/env.h" 7 #include "spdk/event.h" 8 #include "spdk/util.h" 9 #include "spdk/string.h" 10 #include "spdk/nvme_spec.h" 11 #include "spdk/nvme.h" 12 #include "spdk/likely.h" 13 #include "spdk/file.h" 14 #include "spdk/util.h" 15 16 #include "spdk/vfio_user_pci.h" 17 #include <linux/vfio.h> 18 #include "spdk/vfio_user_spec.h" 19 20 #define VFIO_MAXIMUM_SPARSE_MMAP_REGIONS 8 21 22 typedef int (*fuzzer_fn)(const uint8_t *data, size_t size, struct vfio_device *dev); 23 struct fuzz_type { 24 fuzzer_fn fn; 25 uint32_t bytes_per_cmd; 26 }; 27 28 #define VFIO_USER_MAX_PAYLOAD_SIZE (4096) 29 static uint8_t payload[VFIO_USER_MAX_PAYLOAD_SIZE]; 30 31 static char *g_ctrlr_path; 32 static char *g_artifact_prefix; 33 static int32_t g_time_in_sec = 10; 34 static char *g_corpus_dir; 35 static uint8_t *g_repro_data; 36 static size_t g_repro_size; 37 static pthread_t g_fuzz_td; 38 static pthread_t g_reactor_td; 39 static struct fuzz_type *g_fuzzer; 40 41 enum IO_POLLER_STATE { 42 IO_POLLER_STATE_IDLE, 43 IO_POLLER_STATE_PROCESSING, 44 IO_POLLER_STATE_TERMINATE_INIT, 45 IO_POLLER_STATE_TERMINATE_WAIT, 46 IO_POLLER_STATE_TERMINATE_DONE, 47 }; 48 49 struct io_thread { 50 enum IO_POLLER_STATE state; 51 int lba_num; 52 char *write_buf; 53 char *read_buf; 54 size_t buf_size; 55 struct spdk_poller *run_poller; 56 struct spdk_thread *thread; 57 struct spdk_nvme_ctrlr *io_ctrlr; 58 pthread_t io_td; 59 pthread_t term_td; 60 struct spdk_nvme_ns *io_ns; 61 struct spdk_nvme_qpair *io_qpair; 62 char *io_ctrlr_path; 63 } g_io_thread; 64 65 static int 66 fuzz_vfio_user_version(const uint8_t *data, size_t size, struct vfio_device *dev) 67 { 68 struct vfio_user_version *version = (struct vfio_user_version *)payload; 69 70 version->major = ((uint16_t)data[0] << 8) + (uint16_t)data[1]; 71 version->minor = ((uint16_t)data[2] << 8) + (uint16_t)data[3]; 72 73 return spdk_vfio_user_dev_send_request(dev, VFIO_USER_VERSION, payload, 74 sizeof(struct vfio_user_version), 75 sizeof(payload), NULL, 0); 76 } 77 78 static int 79 fuzz_vfio_user_region_rw(const uint8_t *data, size_t size, struct vfio_device *dev) 80 { 81 uint8_t buf[4]; 82 uint64_t offset = 0; 83 84 offset = ((uint64_t)data[0] << 8) + (uint64_t)data[1]; 85 offset = (SPDK_ALIGN_FLOOR(offset, 4)) % 4096; 86 memcpy(buf, &data[2], sizeof(buf)); 87 88 /* writes to BAR0 depending on the register, therefore the return value is never checked */ 89 spdk_vfio_user_pci_bar_access(dev, VFIO_PCI_BAR0_REGION_INDEX, offset, sizeof(buf), 90 &buf, true); 91 return spdk_vfio_user_pci_bar_access(dev, VFIO_PCI_BAR0_REGION_INDEX, offset, sizeof(buf), 92 &buf, false); 93 } 94 95 static struct fuzz_type g_fuzzers[] = { 96 { .fn = fuzz_vfio_user_region_rw, .bytes_per_cmd = 6}, 97 { .fn = fuzz_vfio_user_version, .bytes_per_cmd = 4}, 98 { .fn = NULL, .bytes_per_cmd = 0} 99 }; 100 101 #define NUM_FUZZERS (SPDK_COUNTOF(g_fuzzers) - 1) 102 103 static int 104 TestOneInput(const uint8_t *data, size_t size) 105 { 106 struct vfio_device *dev = NULL; 107 char ctrlr_path[PATH_MAX]; 108 int ret = 0; 109 110 if (size < g_fuzzer->bytes_per_cmd) { 111 return -1; 112 } 113 114 snprintf(ctrlr_path, sizeof(ctrlr_path), "%s/cntrl", g_ctrlr_path); 115 ret = access(ctrlr_path, F_OK); 116 if (ret != 0) { 117 fprintf(stderr, "Access path %s failed\n", ctrlr_path); 118 spdk_app_stop(-1); 119 return -1; 120 } 121 122 dev = spdk_vfio_user_setup(ctrlr_path); 123 if (dev == NULL) { 124 fprintf(stderr, "spdk_vfio_user_setup() failed for controller path '%s'\n", 125 ctrlr_path); 126 spdk_app_stop(-1); 127 return -1; 128 } 129 130 /* run cmds here */ 131 if (g_fuzzer->fn != NULL) { 132 g_fuzzer->fn(data, size, dev); 133 } 134 135 spdk_vfio_user_release(dev); 136 return 0; 137 } 138 139 int LLVMFuzzerRunDriver(int *argc, char ***argv, int (*UserCb)(const uint8_t *Data, size_t Size)); 140 141 static void 142 io_terminate(void *ctx) 143 { 144 ((struct io_thread *)ctx)->state = IO_POLLER_STATE_TERMINATE_INIT; 145 } 146 147 static void 148 exit_handler(void) 149 { 150 if (g_io_thread.io_ctrlr_path && g_io_thread.thread) { 151 spdk_thread_send_msg(g_io_thread.thread, io_terminate, &g_io_thread); 152 153 } else if (spdk_thread_get_app_thread()) { 154 spdk_app_stop(0); 155 } 156 157 pthread_join(g_reactor_td, NULL); 158 } 159 160 static void * 161 start_fuzzer(void *ctx) 162 { 163 char *_argv[] = { 164 "spdk", 165 "-len_control=0", 166 /* TODO: temporarily disable leak detection due to issues 167 * with ASAN and DPDK, see #2455 and #2992. 168 */ 169 "-detect_leaks=0", 170 NULL, 171 NULL, 172 NULL, 173 NULL 174 }; 175 char time_str[128]; 176 char prefix[PATH_MAX]; 177 char len_str[128]; 178 char **argv = _argv; 179 int argc = SPDK_COUNTOF(_argv); 180 uint32_t len = 0; 181 182 spdk_unaffinitize_thread(); 183 snprintf(prefix, sizeof(prefix), "-artifact_prefix=%s", g_artifact_prefix); 184 argv[argc - 4] = prefix; 185 len = 10 * g_fuzzer->bytes_per_cmd; 186 snprintf(len_str, sizeof(len_str), "-max_len=%d", len); 187 argv[argc - 3] = len_str; 188 snprintf(time_str, sizeof(time_str), "-max_total_time=%d", g_time_in_sec); 189 argv[argc - 2] = time_str; 190 argv[argc - 1] = g_corpus_dir; 191 192 atexit(exit_handler); 193 194 free(g_artifact_prefix); 195 196 if (g_repro_data) { 197 printf("Running single test based on reproduction data file.\n"); 198 TestOneInput(g_repro_data, g_repro_size); 199 printf("Done.\n"); 200 } else { 201 LLVMFuzzerRunDriver(&argc, &argv, TestOneInput); 202 /* TODO: in the normal case, LLVMFuzzerRunDriver never returns - it calls exit() 203 * directly and we never get here. But this behavior isn't really documented 204 * anywhere by LLVM. 205 */ 206 } 207 208 return NULL; 209 } 210 211 static void 212 read_complete(void *arg, const struct spdk_nvme_cpl *completion) 213 { 214 int sectors_num = 0; 215 struct io_thread *io = (struct io_thread *)arg; 216 217 if (spdk_nvme_cpl_is_error(completion)) { 218 spdk_nvme_qpair_print_completion(io->io_qpair, (struct spdk_nvme_cpl *)completion); 219 fprintf(stderr, "I/O read error status: %s\n", 220 spdk_nvme_cpl_get_status_string(&completion->status)); 221 io->state = IO_POLLER_STATE_TERMINATE_WAIT; 222 pthread_kill(g_fuzz_td, SIGSEGV); 223 return; 224 } 225 226 if (memcmp(io->read_buf, io->write_buf, io->buf_size)) { 227 fprintf(stderr, "I/O corrupt, value not the same\n"); 228 io->state = IO_POLLER_STATE_TERMINATE_WAIT; 229 pthread_kill(g_fuzz_td, SIGSEGV); 230 return; 231 } 232 233 sectors_num = spdk_nvme_ns_get_num_sectors(io->io_ns); 234 io->lba_num = (io->lba_num + 1) % sectors_num; 235 if (io->state != IO_POLLER_STATE_TERMINATE_INIT) { 236 io->state = IO_POLLER_STATE_IDLE; 237 } 238 } 239 240 static void 241 write_complete(void *arg, const struct spdk_nvme_cpl *completion) 242 { 243 int rc = 0; 244 struct io_thread *io = (struct io_thread *)arg; 245 246 if (spdk_nvme_cpl_is_error(completion)) { 247 spdk_nvme_qpair_print_completion(io->io_qpair, 248 (struct spdk_nvme_cpl *)completion); 249 fprintf(stderr, "I/O write error status: %s\n", 250 spdk_nvme_cpl_get_status_string(&completion->status)); 251 io->state = IO_POLLER_STATE_TERMINATE_WAIT; 252 pthread_kill(g_fuzz_td, SIGSEGV); 253 return; 254 } 255 rc = spdk_nvme_ns_cmd_read(io->io_ns, io->io_qpair, 256 io->read_buf, io->lba_num, 1, 257 read_complete, io, 0); 258 if (rc != 0) { 259 fprintf(stderr, "starting read I/O failed\n"); 260 io->state = IO_POLLER_STATE_TERMINATE_WAIT; 261 pthread_kill(g_fuzz_td, SIGSEGV); 262 } 263 } 264 265 static void * 266 terminate_io_thread(void *ctx) 267 { 268 struct io_thread *io = (struct io_thread *)ctx; 269 270 spdk_nvme_ctrlr_free_io_qpair(io->io_qpair); 271 spdk_nvme_detach(io->io_ctrlr); 272 spdk_free(io->write_buf); 273 spdk_free(io->read_buf); 274 275 io->state = IO_POLLER_STATE_TERMINATE_DONE; 276 277 return NULL; 278 } 279 280 static int 281 io_poller(void *ctx) 282 { 283 int ret = 0; 284 struct io_thread *io = (struct io_thread *)ctx; 285 size_t i; 286 unsigned int seed = 0; 287 int *write_buf = (int *)io->write_buf; 288 289 switch (io->state) { 290 case IO_POLLER_STATE_IDLE: 291 break; 292 case IO_POLLER_STATE_PROCESSING: 293 spdk_nvme_qpair_process_completions(io->io_qpair, 0); 294 return SPDK_POLLER_BUSY; 295 case IO_POLLER_STATE_TERMINATE_INIT: 296 if (spdk_nvme_qpair_get_num_outstanding_reqs(io->io_qpair) > 0) { 297 spdk_nvme_qpair_process_completions(io->io_qpair, 0); 298 return SPDK_POLLER_BUSY; 299 } 300 301 io->state = IO_POLLER_STATE_TERMINATE_WAIT; 302 ret = pthread_create(&io->term_td, NULL, terminate_io_thread, ctx); 303 if (ret != 0) { 304 abort(); 305 } 306 return SPDK_POLLER_BUSY; 307 case IO_POLLER_STATE_TERMINATE_WAIT: 308 return SPDK_POLLER_BUSY; 309 case IO_POLLER_STATE_TERMINATE_DONE: 310 spdk_poller_unregister(&io->run_poller); 311 spdk_thread_exit(spdk_get_thread()); 312 spdk_app_stop(0); 313 return SPDK_POLLER_IDLE; 314 default: 315 break; 316 } 317 318 io->state = IO_POLLER_STATE_PROCESSING; 319 320 /* Compiler should optimize the "/ sizeof(int)" into a right shift. */ 321 for (i = 0; i < io->buf_size / sizeof(int); i++) { 322 write_buf[i] = rand_r(&seed); 323 } 324 325 ret = spdk_nvme_ns_cmd_write(io->io_ns, io->io_qpair, 326 io->write_buf, io->lba_num, 1, 327 write_complete, io, 0); 328 if (ret < 0) { 329 fprintf(stderr, "starting write I/O failed\n"); 330 pthread_kill(g_fuzz_td, SIGSEGV); 331 return SPDK_POLLER_IDLE; 332 } 333 334 return SPDK_POLLER_IDLE; 335 } 336 337 static void 338 start_io_poller(void *ctx) 339 { 340 struct io_thread *io = (struct io_thread *)ctx; 341 342 io->run_poller = SPDK_POLLER_REGISTER(io_poller, ctx, 0); 343 if (io->run_poller == NULL) { 344 fprintf(stderr, "Failed to register a poller for IO.\n"); 345 spdk_app_stop(-1); 346 pthread_kill(g_fuzz_td, SIGSEGV); 347 } 348 } 349 350 static void * 351 init_io(void *ctx) 352 { 353 struct spdk_nvme_transport_id trid = {}; 354 int nsid = 0; 355 356 snprintf(trid.traddr, sizeof(trid.traddr), "%s", g_io_thread.io_ctrlr_path); 357 358 trid.trtype = SPDK_NVME_TRANSPORT_VFIOUSER; 359 g_io_thread.io_ctrlr = spdk_nvme_connect(&trid, NULL, 0); 360 if (g_io_thread.io_ctrlr == NULL) { 361 fprintf(stderr, "spdk_nvme_connect() failed for transport address '%s'\n", 362 trid.traddr); 363 spdk_app_stop(-1); 364 pthread_kill(g_fuzz_td, SIGSEGV); 365 return NULL; 366 } 367 368 g_io_thread.io_qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_io_thread.io_ctrlr, NULL, 0); 369 if (g_io_thread.io_qpair == NULL) { 370 spdk_nvme_detach(g_io_thread.io_ctrlr); 371 fprintf(stderr, "spdk_nvme_ctrlr_alloc_io_qpair failed\n"); 372 spdk_app_stop(-1); 373 pthread_kill(g_fuzz_td, SIGSEGV); 374 return NULL; 375 } 376 377 if (spdk_nvme_ctrlr_get_num_ns(g_io_thread.io_ctrlr) == 0) { 378 fprintf(stderr, "no namespaces for IO\n"); 379 spdk_app_stop(-1); 380 pthread_kill(g_fuzz_td, SIGSEGV); 381 return NULL; 382 } 383 384 nsid = spdk_nvme_ctrlr_get_first_active_ns(g_io_thread.io_ctrlr); 385 g_io_thread.io_ns = spdk_nvme_ctrlr_get_ns(g_io_thread.io_ctrlr, nsid); 386 if (!g_io_thread.io_ns) { 387 fprintf(stderr, "no io_ns for IO\n"); 388 spdk_app_stop(-1); 389 pthread_kill(g_fuzz_td, SIGSEGV); 390 return NULL; 391 } 392 393 g_io_thread.buf_size = spdk_nvme_ns_get_sector_size(g_io_thread.io_ns); 394 395 g_io_thread.read_buf = spdk_zmalloc(g_io_thread.buf_size, 0x1000, NULL, 396 SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 397 398 g_io_thread.write_buf = spdk_zmalloc(g_io_thread.buf_size, 0x1000, NULL, 399 SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); 400 401 if (!g_io_thread.write_buf || !g_io_thread.read_buf) { 402 fprintf(stderr, "cannot allocated memory for io buffers\n"); 403 spdk_app_stop(-1); 404 pthread_kill(g_fuzz_td, SIGSEGV); 405 return NULL; 406 } 407 408 g_io_thread.thread = spdk_thread_create("io_thread", NULL); 409 if (g_io_thread.thread == NULL) { 410 fprintf(stderr, "cannot create io thread\n"); 411 spdk_app_stop(-1); 412 pthread_kill(g_fuzz_td, SIGSEGV); 413 return NULL; 414 } 415 416 spdk_thread_send_msg(g_io_thread.thread, start_io_poller, &g_io_thread); 417 418 return NULL; 419 } 420 421 static void 422 begin_fuzz(void *ctx) 423 { 424 int rc = 0; 425 426 g_reactor_td = pthread_self(); 427 428 rc = pthread_create(&g_fuzz_td, NULL, start_fuzzer, NULL); 429 if (rc != 0) { 430 spdk_app_stop(-1); 431 return; 432 } 433 434 /* posix thread is use to avoid deadlock during spdk_nvme_connect 435 * vfio-user version negotiation may block when waiting for response 436 */ 437 if (g_io_thread.io_ctrlr_path) { 438 rc = pthread_create(&g_io_thread.io_td, NULL, init_io, NULL); 439 if (rc != 0) { 440 spdk_app_stop(-1); 441 pthread_kill(g_fuzz_td, SIGSEGV); 442 } 443 } 444 } 445 446 static void 447 vfio_fuzz_usage(void) 448 { 449 fprintf(stderr, " -D Path of corpus directory.\n"); 450 fprintf(stderr, " -F Path for ctrlr that should be fuzzed.\n"); 451 fprintf(stderr, " -N Name of reproduction data file.\n"); 452 fprintf(stderr, " -P Provide a prefix to use when saving artifacts.\n"); 453 fprintf(stderr, " -t Time to run fuzz tests (in seconds). Default: 10\n"); 454 fprintf(stderr, " -Y Path of addition controller to perform io.\n"); 455 fprintf(stderr, " -Z Fuzzer to run (0 to %lu)\n", NUM_FUZZERS - 1); 456 } 457 458 static int 459 vfio_fuzz_parse(int ch, char *arg) 460 { 461 long long tmp = 0; 462 FILE *repro_file = NULL; 463 464 switch (ch) { 465 case 'D': 466 g_corpus_dir = strdup(optarg); 467 if (!g_corpus_dir) { 468 fprintf(stderr, "cannot strdup: %s\n", optarg); 469 return -ENOMEM; 470 } 471 break; 472 case 'F': 473 g_ctrlr_path = strdup(optarg); 474 if (!g_ctrlr_path) { 475 fprintf(stderr, "cannot strdup: %s\n", optarg); 476 return -ENOMEM; 477 } 478 break; 479 case 'N': 480 repro_file = fopen(optarg, "r"); 481 if (repro_file == NULL) { 482 fprintf(stderr, "could not open %s: %s\n", optarg, spdk_strerror(errno)); 483 return -1; 484 } 485 g_repro_data = spdk_posix_file_load(repro_file, &g_repro_size); 486 if (g_repro_data == NULL) { 487 fprintf(stderr, "could not load data for file %s\n", optarg); 488 return -1; 489 } 490 break; 491 case 'P': 492 g_artifact_prefix = strdup(optarg); 493 if (!g_artifact_prefix) { 494 fprintf(stderr, "cannot strdup: %s\n", optarg); 495 return -ENOMEM; 496 } 497 break; 498 case 'Y': 499 g_io_thread.io_ctrlr_path = strdup(optarg); 500 if (!g_io_thread.io_ctrlr_path) { 501 fprintf(stderr, "cannot strdup: %s\n", optarg); 502 return -ENOMEM; 503 } 504 break; 505 case 't': 506 case 'Z': 507 tmp = spdk_strtoll(optarg, 10); 508 if (tmp < 0 || tmp >= INT_MAX) { 509 fprintf(stderr, "Invalid value '%s' for option -%c.\n", optarg, ch); 510 return -EINVAL; 511 } 512 switch (ch) { 513 case 't': 514 g_time_in_sec = tmp; 515 break; 516 case 'Z': 517 if ((unsigned long)tmp >= NUM_FUZZERS) { 518 fprintf(stderr, "Invalid fuzz type %lld (max %lu)\n", tmp, NUM_FUZZERS - 1); 519 return -EINVAL; 520 } 521 g_fuzzer = &g_fuzzers[tmp]; 522 break; 523 } 524 break; 525 case '?': 526 default: 527 return -EINVAL; 528 } 529 return 0; 530 } 531 532 static void 533 fuzz_shutdown(void) 534 { 535 /* If the user terminates the fuzzer prematurely, it is likely due 536 * to an input hang. So raise a SIGSEGV signal which will cause the 537 * fuzzer to generate a crash file for the last input. 538 * 539 * Note that the fuzzer will always generate a crash file, even if 540 * we get our TestOneInput() function (which is called by the fuzzer) 541 * to pthread_exit(). So just doing the SIGSEGV here in all cases is 542 * simpler than trying to differentiate between hung inputs and 543 * an impatient user. 544 */ 545 pthread_kill(g_fuzz_td, SIGSEGV); 546 } 547 548 int 549 main(int argc, char **argv) 550 { 551 struct spdk_app_opts opts = {}; 552 int rc = 0; 553 554 spdk_app_opts_init(&opts, sizeof(opts)); 555 opts.name = "vfio_fuzz"; 556 opts.shutdown_cb = fuzz_shutdown; 557 558 if ((rc = spdk_app_parse_args(argc, argv, &opts, "D:F:N:P:t:Y:Z:", NULL, vfio_fuzz_parse, 559 vfio_fuzz_usage) != SPDK_APP_PARSE_ARGS_SUCCESS)) { 560 return rc; 561 } 562 563 if (!g_corpus_dir) { 564 fprintf(stderr, "Must specify corpus dir with -D option\n"); 565 return -1; 566 } 567 568 if (!g_ctrlr_path) { 569 fprintf(stderr, "Must specify ctrlr path with -F option\n"); 570 return -1; 571 } 572 573 if (!g_fuzzer) { 574 fprintf(stderr, "Must specify fuzzer with -Z option\n"); 575 return -1; 576 } 577 578 rc = spdk_app_start(&opts, begin_fuzz, NULL); 579 580 spdk_app_fini(); 581 return rc; 582 } 583