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