xref: /spdk/test/app/fuzz/llvm_vfio_fuzz/llvm_vfio_fuzz.c (revision c680e3a05b1a903c18bf3f75b732765607126f45)
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