Branch data Line data Source code
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 : : #include "spdk/config.h"
20 : :
21 : : #ifdef SPDK_CONFIG_ASAN
22 : : #include <sanitizer/lsan_interface.h>
23 : : #endif
24 : : #define VFIO_MAXIMUM_SPARSE_MMAP_REGIONS 8
25 : : #define VFIO_USER_GET_REGION_INFO_LEN 4096
26 : :
27 : : typedef int (*fuzzer_fn)(const uint8_t *data, size_t size, struct vfio_device *dev);
28 : : struct fuzz_type {
29 : : fuzzer_fn fn;
30 : : uint32_t bytes_per_cmd;
31 : : };
32 : :
33 : : #define VFIO_USER_MAX_PAYLOAD_SIZE (4096)
34 : : static uint8_t payload[VFIO_USER_MAX_PAYLOAD_SIZE];
35 : :
36 : : static char *g_ctrlr_path;
37 : : static char *g_artifact_prefix;
38 : : static int32_t g_time_in_sec = 10;
39 : : static char *g_corpus_dir;
40 : : static uint8_t *g_repro_data;
41 : : static size_t g_repro_size;
42 : : static pthread_t g_fuzz_td;
43 : : static pthread_t g_reactor_td;
44 : : static struct fuzz_type *g_fuzzer;
45 : :
46 : : enum IO_POLLER_STATE {
47 : : IO_POLLER_STATE_IDLE,
48 : : IO_POLLER_STATE_PROCESSING,
49 : : IO_POLLER_STATE_TERMINATE_INIT,
50 : : IO_POLLER_STATE_TERMINATE_WAIT,
51 : : IO_POLLER_STATE_TERMINATE_DONE,
52 : : };
53 : :
54 : : struct io_thread {
55 : : enum IO_POLLER_STATE state;
56 : : int lba_num;
57 : : char *write_buf;
58 : : char *read_buf;
59 : : size_t buf_size;
60 : : struct spdk_poller *run_poller;
61 : : struct spdk_thread *thread;
62 : : struct spdk_nvme_ctrlr *io_ctrlr;
63 : : pthread_t io_td;
64 : : pthread_t term_td;
65 : : struct spdk_nvme_ns *io_ns;
66 : : struct spdk_nvme_qpair *io_qpair;
67 : : char *io_ctrlr_path;
68 : : } g_io_thread;
69 : :
70 : : static int
71 : 10 : fuzz_vfio_user_version(const uint8_t *data, size_t size, struct vfio_device *dev)
72 : : {
73 : 10 : struct vfio_user_version *version = (struct vfio_user_version *)payload;
74 : :
75 [ + - + - : 10 : version->major = ((uint16_t)data[0] << 8) + (uint16_t)data[1];
- + + - +
- + - + -
+ - + - ]
76 [ + - + - : 10 : version->minor = ((uint16_t)data[2] << 8) + (uint16_t)data[3];
- + + - +
- + - + -
+ - + - ]
77 : :
78 : 10 : return spdk_vfio_user_dev_send_request(dev, VFIO_USER_VERSION, payload,
79 : : sizeof(struct vfio_user_version),
80 : : sizeof(payload), NULL, 0);
81 : : }
82 : :
83 : : static int
84 : 9 : fuzz_vfio_user_region_rw(const uint8_t *data, size_t size, struct vfio_device *dev)
85 : : {
86 : : uint8_t buf[4];
87 : 9 : uint64_t offset = 0;
88 : :
89 [ + - + - : 9 : offset = ((uint64_t)data[0] << 8) + (uint64_t)data[1];
+ - + - +
- ]
90 [ + - ]: 9 : offset = (SPDK_ALIGN_FLOOR(offset, 4)) % 4096;
91 [ + - + - : 9 : memcpy(buf, &data[2], sizeof(buf));
+ - ]
92 : :
93 : : /* writes to BAR0 depending on the register, therefore the return value is never checked */
94 : 9 : spdk_vfio_user_pci_bar_access(dev, VFIO_PCI_BAR0_REGION_INDEX, offset, sizeof(buf),
95 : : &buf, true);
96 : 9 : return spdk_vfio_user_pci_bar_access(dev, VFIO_PCI_BAR0_REGION_INDEX, offset, sizeof(buf),
97 : : &buf, false);
98 : : }
99 : :
100 : : #define VFIO_USER_GET_REGION_INFO_LEN 4096
101 : :
102 : : static int
103 : 10 : fuzz_vfio_user_get_region_info(const uint8_t *data, size_t size, struct vfio_device *dev)
104 : : {
105 : 10 : int ret = 0;
106 : : int fds[VFIO_MAXIMUM_SPARSE_MMAP_REGIONS];
107 : : uint8_t buf[VFIO_USER_GET_REGION_INFO_LEN];
108 : 10 : struct vfio_region_info *info = (struct vfio_region_info *)buf;
109 : :
110 [ + - + - : 10 : memcpy(&info->index, &data[0], 4);
+ - + - ]
111 [ + - + - : 10 : memcpy(&info->argsz, &data[4], 4);
+ - + - ]
112 : :
113 : 20 : ret = spdk_vfio_user_dev_send_request(dev, VFIO_USER_DEVICE_GET_REGION_INFO,
114 [ + - + - ]: 10 : info, info->argsz, VFIO_USER_GET_REGION_INFO_LEN, fds,
115 : : VFIO_MAXIMUM_SPARSE_MMAP_REGIONS);
116 : 10 : return ret;
117 : : }
118 : :
119 : : /* Since both ends of the connection are in the same process,
120 : : * picking completely random addresses is actually fine, since
121 : : * we won't be actually mapping anything.
122 : : */
123 : : static int
124 : 10 : fuzz_vfio_user_dma_map(const uint8_t *data, size_t size, struct vfio_device *dev)
125 : : {
126 : 10 : struct vfio_user_dma_map dma_map = { 0 };
127 : : int fd;
128 : :
129 [ + - + - : 10 : memcpy(&fd, &data[0], 4);
+ - ]
130 : 10 : dma_map.argsz = sizeof(struct vfio_user_dma_map);
131 : :
132 [ + - + - : 10 : memcpy(&dma_map.addr, &data[8], 8);
+ - ]
133 [ + - + - : 10 : memcpy(&dma_map.size, &data[16], 8);
+ - ]
134 [ + - + - : 10 : memcpy(&dma_map.offset, &data[24], 8);
+ - ]
135 : :
136 [ - + + - : 10 : dma_map.flags = VFIO_USER_F_DMA_REGION_READ | VFIO_USER_F_DMA_REGION_WRITE;
- + + - ]
137 : :
138 : 10 : spdk_vfio_user_dev_send_request(dev, VFIO_USER_DMA_MAP,
139 : : &dma_map, sizeof(dma_map), sizeof(dma_map), &fd, 1);
140 : 10 : return 0;
141 : : }
142 : :
143 : : static int
144 : 10 : fuzz_vfio_user_dma_unmap(const uint8_t *data, size_t size, struct vfio_device *dev)
145 : : {
146 : 10 : struct vfio_user_dma_unmap dma_unmap = { 0 };
147 : 10 : struct vfio_user_dma_map dma_map = { 0 };
148 : : int fd;
149 : :
150 [ + - + - : 10 : memcpy(&fd, &data[0], 4);
+ - ]
151 : 10 : dma_map.argsz = sizeof(struct vfio_user_dma_map);
152 : :
153 [ + - + - : 10 : memcpy(&dma_map.addr, &data[8], 8);
+ - ]
154 [ + - + - : 10 : memcpy(&dma_map.size, &data[16], 8);
+ - ]
155 [ + - + - : 10 : memcpy(&dma_map.offset, &data[24], 8);
+ - ]
156 : :
157 [ - + + - : 10 : dma_map.flags = VFIO_USER_F_DMA_REGION_READ | VFIO_USER_F_DMA_REGION_WRITE;
- + + - ]
158 : :
159 : 10 : dma_unmap.argsz = sizeof(struct vfio_user_dma_unmap);
160 [ + - ]: 10 : dma_unmap.addr = dma_map.addr;
161 [ + - ]: 10 : dma_unmap.size = dma_map.size;
162 : :
163 : 10 : spdk_vfio_user_dev_send_request(dev, VFIO_USER_DMA_MAP,
164 : : &dma_map, sizeof(dma_map), sizeof(dma_map), &fd, 1);
165 : : /* Don't verify return value to check unmapping not previously mapped region */
166 : 10 : spdk_vfio_user_dev_send_request(dev, VFIO_USER_DMA_UNMAP,
167 : : &dma_unmap, sizeof(dma_unmap), sizeof(dma_unmap), &fd, 1);
168 : 10 : return 0;
169 : : }
170 : : static int
171 : 9 : fuzz_vfio_user_irq_set(const uint8_t *data, size_t size, struct vfio_device *dev)
172 : : {
173 : : uint8_t buf[VFIO_USER_GET_REGION_INFO_LEN];
174 : 9 : struct vfio_irq_set *irq_set = (struct vfio_irq_set *)buf;
175 : :
176 [ + - + - ]: 9 : irq_set->argsz = sizeof(struct vfio_irq_set) ;
177 [ + - + - : 9 : memcpy(&irq_set->flags, &data[0], 4);
+ - + - ]
178 [ + - + - : 9 : irq_set->index = data[4]; /* VFIO_PCI_NUM_IRQS */
+ - + - ]
179 [ + - + - : 9 : memcpy(&irq_set->start, &data[5], 4);
+ - + - ]
180 [ + - + - : 9 : memcpy(&irq_set->count, &data[9], 4);
+ - + - ]
181 : :
182 : 18 : spdk_vfio_user_dev_send_request(dev, VFIO_USER_DEVICE_SET_IRQS,
183 [ + - + - ]: 9 : irq_set, irq_set->argsz,
184 : : VFIO_USER_GET_REGION_INFO_LEN, NULL, 0);
185 : 9 : return 0;
186 : : }
187 : :
188 : : static int
189 : 9 : fuzz_vfio_user_set_msix(const uint8_t *data, size_t size, struct vfio_device *dev)
190 : : {
191 : : struct vfio_irq_set irq_set;
192 : :
193 : 9 : irq_set.argsz = sizeof(struct vfio_irq_set);
194 : : /* Max value is VFIO_IRQ_SET_ACTION_TRIGGER, try different combination too */
195 [ + - + - : 9 : irq_set.flags = data[0] & ((1 << 6) - 1);
- + + - +
- ]
196 [ + - ]: 9 : irq_set.index = VFIO_PCI_MSIX_IRQ_INDEX;
197 [ + - + - : 9 : memcpy(&irq_set.start, &data[2], 4);
+ - ]
198 [ + - + - : 9 : memcpy(&irq_set.count, &data[6], 4);
+ - ]
199 : :
200 : 9 : spdk_vfio_user_dev_send_request(dev, VFIO_USER_DEVICE_SET_IRQS,
201 : : &irq_set, sizeof(irq_set), sizeof(irq_set), NULL, 0);
202 : 9 : return 0;
203 : : }
204 : :
205 : : static struct fuzz_type g_fuzzers[] = {
206 : : { .fn = fuzz_vfio_user_region_rw, .bytes_per_cmd = 6},
207 : : { .fn = fuzz_vfio_user_version, .bytes_per_cmd = 4},
208 : : { .fn = fuzz_vfio_user_get_region_info, .bytes_per_cmd = 8},
209 : : { .fn = fuzz_vfio_user_dma_map, .bytes_per_cmd = 32},
210 : : { .fn = fuzz_vfio_user_dma_unmap, .bytes_per_cmd = 32},
211 : : { .fn = fuzz_vfio_user_irq_set, .bytes_per_cmd = 12},
212 : : { .fn = fuzz_vfio_user_set_msix, .bytes_per_cmd = 9},
213 : : { .fn = NULL, .bytes_per_cmd = 0}
214 : : };
215 : :
216 : : #define NUM_FUZZERS (SPDK_COUNTOF(g_fuzzers) - 1)
217 : :
218 : : static int
219 : 190 : TestOneInput(const uint8_t *data, size_t size)
220 : : {
221 : 190 : struct vfio_device *dev = NULL;
222 : : char ctrlr_path[PATH_MAX];
223 : 190 : int ret = 0;
224 : :
225 [ + - + - : 190 : if (size < g_fuzzer->bytes_per_cmd) {
+ + ]
226 : 123 : return -1;
227 : : }
228 : :
229 : 67 : snprintf(ctrlr_path, sizeof(ctrlr_path), "%s/cntrl", g_ctrlr_path);
230 [ + - ]: 67 : ret = access(ctrlr_path, F_OK);
231 [ - + ]: 67 : if (ret != 0) {
232 : 0 : fprintf(stderr, "Access path %s failed\n", ctrlr_path);
233 : 0 : spdk_app_stop(-1);
234 : 0 : return -1;
235 : : }
236 : :
237 : 67 : dev = spdk_vfio_user_setup(ctrlr_path);
238 [ + - ]: 67 : if (dev == NULL) {
239 : 0 : fprintf(stderr, "spdk_vfio_user_setup() failed for controller path '%s'\n",
240 : 0 : ctrlr_path);
241 : 0 : spdk_app_stop(-1);
242 : 0 : return -1;
243 : : }
244 : :
245 : : /* run cmds here */
246 [ + - + - : 67 : if (g_fuzzer->fn != NULL) {
+ - ]
247 [ + - + - : 67 : g_fuzzer->fn(data, size, dev);
- + + - ]
248 : 67 : }
249 : :
250 : 67 : spdk_vfio_user_release(dev);
251 : 67 : return 0;
252 : 190 : }
253 : :
254 : : int LLVMFuzzerRunDriver(int *argc, char ***argv, int (*UserCb)(const uint8_t *Data, size_t Size));
255 : :
256 : : static void
257 : 7 : io_terminate(void *ctx)
258 : : {
259 [ + - + - ]: 7 : ((struct io_thread *)ctx)->state = IO_POLLER_STATE_TERMINATE_INIT;
260 : 7 : }
261 : :
262 : : static void
263 : 7 : exit_handler(void)
264 : : {
265 [ + - + - : 7 : if (g_io_thread.io_ctrlr_path && g_io_thread.thread) {
+ - - + ]
266 [ + - ]: 7 : spdk_thread_send_msg(g_io_thread.thread, io_terminate, &g_io_thread);
267 : :
268 [ # # ]: 7 : } else if (spdk_thread_get_app_thread()) {
269 : 0 : spdk_app_stop(0);
270 : 0 : }
271 : :
272 : 7 : pthread_join(g_reactor_td, NULL);
273 : 7 : }
274 : :
275 : : static void *
276 : 7 : start_fuzzer(void *ctx)
277 : : {
278 : 7 : char *_argv[] = {
279 : : "spdk",
280 : : "-len_control=0",
281 : : "-detect_leaks=1",
282 : : NULL,
283 : : NULL,
284 : : NULL,
285 : : NULL
286 : : };
287 : : char time_str[128];
288 : : char prefix[PATH_MAX];
289 : : char len_str[128];
290 : 7 : char **argv = _argv;
291 : 7 : int argc = SPDK_COUNTOF(_argv);
292 : 7 : uint32_t len = 0;
293 : :
294 : 7 : spdk_unaffinitize_thread();
295 : 7 : snprintf(prefix, sizeof(prefix), "-artifact_prefix=%s", g_artifact_prefix);
296 [ + - + - : 7 : argv[argc - 4] = prefix;
+ - ]
297 [ + - + - ]: 7 : len = 10 * g_fuzzer->bytes_per_cmd;
298 : 7 : snprintf(len_str, sizeof(len_str), "-max_len=%d", len);
299 [ + - + - : 7 : argv[argc - 3] = len_str;
+ - ]
300 : 7 : snprintf(time_str, sizeof(time_str), "-max_total_time=%d", g_time_in_sec);
301 [ + - + - : 7 : argv[argc - 2] = time_str;
+ - ]
302 [ + - + - : 7 : argv[argc - 1] = g_corpus_dir;
+ - ]
303 : :
304 [ + - ]: 7 : atexit(exit_handler);
305 : :
306 : 7 : free(g_artifact_prefix);
307 : :
308 [ - + ]: 7 : if (g_repro_data) {
309 : 0 : printf("Running single test based on reproduction data file.\n");
310 : 0 : TestOneInput(g_repro_data, g_repro_size);
311 : 0 : printf("Done.\n");
312 : 0 : } else {
313 : 7 : LLVMFuzzerRunDriver(&argc, &argv, TestOneInput);
314 : : /* TODO: in the normal case, LLVMFuzzerRunDriver never returns - it calls exit()
315 : : * directly and we never get here. But this behavior isn't really documented
316 : : * anywhere by LLVM.
317 : : */
318 : : }
319 : :
320 : 7 : return NULL;
321 : : }
322 : :
323 : : static void
324 : 73843 : read_complete(void *arg, const struct spdk_nvme_cpl *completion)
325 : : {
326 : 73843 : int sectors_num = 0;
327 : 73843 : struct io_thread *io = (struct io_thread *)arg;
328 : :
329 [ + - + - : 73843 : if (spdk_nvme_cpl_is_error(completion)) {
+ - + - +
- + - + -
- + ]
330 [ # # # # ]: 0 : spdk_nvme_qpair_print_completion(io->io_qpair, (struct spdk_nvme_cpl *)completion);
331 : 0 : fprintf(stderr, "I/O read error status: %s\n",
332 [ # # # # ]: 0 : spdk_nvme_cpl_get_status_string(&completion->status));
333 [ # # # # ]: 0 : io->state = IO_POLLER_STATE_TERMINATE_WAIT;
334 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
335 : 0 : return;
336 : : }
337 : :
338 [ + - + - : 73843 : if (memcmp(io->read_buf, io->write_buf, io->buf_size)) {
+ - + - +
- + - + -
+ - + - ]
339 : 0 : fprintf(stderr, "I/O corrupt, value not the same\n");
340 [ # # # # ]: 0 : io->state = IO_POLLER_STATE_TERMINATE_WAIT;
341 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
342 : 0 : return;
343 : : }
344 : :
345 [ + - + - ]: 73843 : sectors_num = spdk_nvme_ns_get_num_sectors(io->io_ns);
346 [ + - + - : 73843 : io->lba_num = (io->lba_num + 1) % sectors_num;
+ - + - +
- + - ]
347 [ + - + - : 73843 : if (io->state != IO_POLLER_STATE_TERMINATE_INIT) {
+ + ]
348 [ + - + - ]: 73836 : io->state = IO_POLLER_STATE_IDLE;
349 : 73836 : }
350 : 73843 : }
351 : :
352 : : static void
353 : 73843 : write_complete(void *arg, const struct spdk_nvme_cpl *completion)
354 : : {
355 : 73843 : int rc = 0;
356 : 73843 : struct io_thread *io = (struct io_thread *)arg;
357 : :
358 [ + - + - : 73843 : if (spdk_nvme_cpl_is_error(completion)) {
+ - + - +
- + - + -
+ - ]
359 [ # # # # ]: 0 : spdk_nvme_qpair_print_completion(io->io_qpair,
360 : 0 : (struct spdk_nvme_cpl *)completion);
361 : 0 : fprintf(stderr, "I/O write error status: %s\n",
362 [ # # # # ]: 0 : spdk_nvme_cpl_get_status_string(&completion->status));
363 [ # # # # ]: 0 : io->state = IO_POLLER_STATE_TERMINATE_WAIT;
364 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
365 : 0 : return;
366 : : }
367 [ + - + - : 147686 : rc = spdk_nvme_ns_cmd_read(io->io_ns, io->io_qpair,
+ - + - ]
368 [ + - + - : 73843 : io->read_buf, io->lba_num, 1,
+ - + - ]
369 : 73843 : read_complete, io, 0);
370 [ + - ]: 73843 : if (rc != 0) {
371 : 0 : fprintf(stderr, "starting read I/O failed\n");
372 [ # # # # ]: 0 : io->state = IO_POLLER_STATE_TERMINATE_WAIT;
373 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
374 : 0 : }
375 : 73843 : }
376 : :
377 : : static void *
378 : 7 : terminate_io_thread(void *ctx)
379 : : {
380 : 7 : struct io_thread *io = (struct io_thread *)ctx;
381 : :
382 [ + - + - ]: 7 : spdk_nvme_ctrlr_free_io_qpair(io->io_qpair);
383 [ + - + - ]: 7 : spdk_nvme_detach(io->io_ctrlr);
384 [ + - + - ]: 7 : spdk_free(io->write_buf);
385 [ + - + - ]: 7 : spdk_free(io->read_buf);
386 : :
387 [ + - + - ]: 7 : io->state = IO_POLLER_STATE_TERMINATE_DONE;
388 : :
389 : 7 : return NULL;
390 : : }
391 : :
392 : : static int
393 : 474143 : io_poller(void *ctx)
394 : : {
395 : 474143 : int ret = 0;
396 : 474143 : struct io_thread *io = (struct io_thread *)ctx;
397 : : size_t i;
398 : 474143 : unsigned int seed = 0;
399 [ + - + - ]: 474143 : int *write_buf = (int *)io->write_buf;
400 : :
401 [ + - + - : 474143 : switch (io->state) {
+ + + - +
+ ]
402 : : case IO_POLLER_STATE_IDLE:
403 : 73843 : break;
404 : : case IO_POLLER_STATE_PROCESSING:
405 [ + - + - ]: 295357 : spdk_nvme_qpair_process_completions(io->io_qpair, 0);
406 : 295357 : return SPDK_POLLER_BUSY;
407 : : case IO_POLLER_STATE_TERMINATE_INIT:
408 [ + - + - : 22 : if (spdk_nvme_qpair_get_num_outstanding_reqs(io->io_qpair) > 0) {
+ + ]
409 [ + - + - ]: 15 : spdk_nvme_qpair_process_completions(io->io_qpair, 0);
410 : 15 : return SPDK_POLLER_BUSY;
411 : : }
412 : :
413 [ + - + - ]: 7 : io->state = IO_POLLER_STATE_TERMINATE_WAIT;
414 [ + - + - : 7 : ret = pthread_create(&io->term_td, NULL, terminate_io_thread, ctx);
+ - ]
415 [ + - ]: 7 : if (ret != 0) {
416 [ # # ]: 0 : abort();
417 : : }
418 : 7 : return SPDK_POLLER_BUSY;
419 : : case IO_POLLER_STATE_TERMINATE_WAIT:
420 : 104914 : return SPDK_POLLER_BUSY;
421 : : case IO_POLLER_STATE_TERMINATE_DONE:
422 [ + - ]: 7 : spdk_poller_unregister(&io->run_poller);
423 : 7 : spdk_thread_exit(spdk_get_thread());
424 : 7 : spdk_app_stop(0);
425 : 7 : return SPDK_POLLER_IDLE;
426 : : default:
427 : 0 : break;
428 : : }
429 : :
430 [ + - + - ]: 73843 : io->state = IO_POLLER_STATE_PROCESSING;
431 : :
432 : : /* Compiler should optimize the "/ sizeof(int)" into a right shift. */
433 [ + - + - : 9525747 : for (i = 0; i < io->buf_size / sizeof(int); i++) {
+ - + + ]
434 [ - + - + ]: 9451904 : write_buf[i] = rand_r(&seed);
435 : 9451904 : }
436 : :
437 [ + - + - : 147686 : ret = spdk_nvme_ns_cmd_write(io->io_ns, io->io_qpair,
+ - + - ]
438 [ + - + - : 73843 : io->write_buf, io->lba_num, 1,
+ - + - ]
439 : 73843 : write_complete, io, 0);
440 [ - + ]: 73843 : if (ret < 0) {
441 : 0 : fprintf(stderr, "starting write I/O failed\n");
442 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
443 : 0 : return SPDK_POLLER_IDLE;
444 : : }
445 : :
446 : 73843 : return SPDK_POLLER_IDLE;
447 : 474143 : }
448 : :
449 : : static void
450 : 7 : start_io_poller(void *ctx)
451 : : {
452 : 7 : struct io_thread *io = (struct io_thread *)ctx;
453 : :
454 [ + - + - ]: 7 : io->run_poller = SPDK_POLLER_REGISTER(io_poller, ctx, 0);
455 [ + - + - : 7 : if (io->run_poller == NULL) {
+ - ]
456 : 0 : fprintf(stderr, "Failed to register a poller for IO.\n");
457 : 0 : spdk_app_stop(-1);
458 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
459 : 0 : }
460 : 7 : }
461 : :
462 : : static void *
463 : 7 : init_io(void *ctx)
464 : : {
465 : 7 : struct spdk_nvme_transport_id trid = {};
466 : 7 : int nsid = 0;
467 : :
468 [ + - ]: 7 : snprintf(trid.traddr, sizeof(trid.traddr), "%s", g_io_thread.io_ctrlr_path);
469 : :
470 [ + - ]: 7 : trid.trtype = SPDK_NVME_TRANSPORT_VFIOUSER;
471 [ + - ]: 7 : g_io_thread.io_ctrlr = spdk_nvme_connect(&trid, NULL, 0);
472 [ + - - + ]: 7 : if (g_io_thread.io_ctrlr == NULL) {
473 : 0 : fprintf(stderr, "spdk_nvme_connect() failed for transport address '%s'\n",
474 : 0 : trid.traddr);
475 : 0 : spdk_app_stop(-1);
476 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
477 : 0 : return NULL;
478 : : }
479 : :
480 : : /* Even if ASan is enabled in DPDK, leak sanitizer has problems detecting
481 : : * references allocated in DPDK-manage memory. This causes LSAN to report
482 : : * a false memory leak when the 'pqpair->stat' variable is allocated on
483 : : * the heap, but the only reference is stored on `qpair` that is DPDK-manage
484 : : * making it not visible for LSAN. */
485 : : #ifdef SPDK_CONFIG_ASAN
486 : : __lsan_disable();
487 : : #endif
488 [ + - + - ]: 7 : g_io_thread.io_qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_io_thread.io_ctrlr, NULL, 0);
489 : : #ifdef SPDK_CONFIG_ASAN
490 : : __lsan_enable();
491 : : #endif
492 [ + - + - ]: 7 : if (g_io_thread.io_qpair == NULL) {
493 [ # # ]: 0 : spdk_nvme_detach(g_io_thread.io_ctrlr);
494 : 0 : fprintf(stderr, "spdk_nvme_ctrlr_alloc_io_qpair failed\n");
495 : 0 : spdk_app_stop(-1);
496 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
497 : 0 : return NULL;
498 : : }
499 : :
500 [ + - + - ]: 7 : if (spdk_nvme_ctrlr_get_num_ns(g_io_thread.io_ctrlr) == 0) {
501 : 0 : fprintf(stderr, "no namespaces for IO\n");
502 : 0 : spdk_app_stop(-1);
503 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
504 : 0 : return NULL;
505 : : }
506 : :
507 [ + - ]: 7 : nsid = spdk_nvme_ctrlr_get_first_active_ns(g_io_thread.io_ctrlr);
508 [ + - + - ]: 7 : g_io_thread.io_ns = spdk_nvme_ctrlr_get_ns(g_io_thread.io_ctrlr, nsid);
509 [ + - - + ]: 7 : if (!g_io_thread.io_ns) {
510 : 0 : fprintf(stderr, "no io_ns for IO\n");
511 : 0 : spdk_app_stop(-1);
512 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
513 : 0 : return NULL;
514 : : }
515 : :
516 [ + - + - ]: 7 : g_io_thread.buf_size = spdk_nvme_ns_get_sector_size(g_io_thread.io_ns);
517 : :
518 [ + - + - ]: 7 : g_io_thread.read_buf = spdk_zmalloc(g_io_thread.buf_size, 0x1000, NULL,
519 : : SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA);
520 : :
521 [ + - + - ]: 7 : g_io_thread.write_buf = spdk_zmalloc(g_io_thread.buf_size, 0x1000, NULL,
522 : : SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA);
523 : :
524 [ + - + - : 7 : if (!g_io_thread.write_buf || !g_io_thread.read_buf) {
+ - - + ]
525 : 0 : fprintf(stderr, "cannot allocated memory for io buffers\n");
526 : 0 : spdk_app_stop(-1);
527 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
528 : 0 : return NULL;
529 : : }
530 : :
531 [ + - ]: 7 : g_io_thread.thread = spdk_thread_create("io_thread", NULL);
532 [ + - + - ]: 7 : if (g_io_thread.thread == NULL) {
533 : 0 : fprintf(stderr, "cannot create io thread\n");
534 : 0 : spdk_app_stop(-1);
535 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
536 : 0 : return NULL;
537 : : }
538 : :
539 [ - + ]: 7 : spdk_thread_send_msg(g_io_thread.thread, start_io_poller, &g_io_thread);
540 : :
541 : 7 : return NULL;
542 : 7 : }
543 : :
544 : : static void
545 : 7 : begin_fuzz(void *ctx)
546 : : {
547 : 7 : int rc = 0;
548 : :
549 : 7 : g_reactor_td = pthread_self();
550 : :
551 [ + - + - ]: 7 : rc = pthread_create(&g_fuzz_td, NULL, start_fuzzer, NULL);
552 [ + - ]: 7 : if (rc != 0) {
553 : 0 : spdk_app_stop(-1);
554 : 0 : return;
555 : : }
556 : :
557 : : /* posix thread is use to avoid deadlock during spdk_nvme_connect
558 : : * vfio-user version negotiation may block when waiting for response
559 : : */
560 [ + - + - ]: 7 : if (g_io_thread.io_ctrlr_path) {
561 [ + - + - ]: 7 : rc = pthread_create(&g_io_thread.io_td, NULL, init_io, NULL);
562 [ + - ]: 7 : if (rc != 0) {
563 : 0 : spdk_app_stop(-1);
564 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
565 : 0 : }
566 : 7 : }
567 : 7 : }
568 : :
569 : : static void
570 : 0 : vfio_fuzz_usage(void)
571 : : {
572 : 0 : fprintf(stderr, " -D Path of corpus directory.\n");
573 : 0 : fprintf(stderr, " -F Path for ctrlr that should be fuzzed.\n");
574 : 0 : fprintf(stderr, " -N Name of reproduction data file.\n");
575 : 0 : fprintf(stderr, " -P Provide a prefix to use when saving artifacts.\n");
576 : 0 : fprintf(stderr, " -t Time to run fuzz tests (in seconds). Default: 10\n");
577 : 0 : fprintf(stderr, " -Y Path of addition controller to perform io.\n");
578 : 0 : fprintf(stderr, " -Z Fuzzer to run (0 to %lu)\n", NUM_FUZZERS - 1);
579 : 0 : }
580 : :
581 : : static int
582 : 42 : vfio_fuzz_parse(int ch, char *arg)
583 : : {
584 : 42 : long long tmp = 0;
585 : 42 : FILE *repro_file = NULL;
586 : :
587 [ + - + + : 42 : switch (ch) {
- + + - ]
588 : : case 'D':
589 [ + - ]: 7 : g_corpus_dir = strdup(optarg);
590 [ + - ]: 7 : if (!g_corpus_dir) {
591 : 0 : fprintf(stderr, "cannot strdup: %s\n", optarg);
592 : 0 : return -ENOMEM;
593 : : }
594 : 7 : break;
595 : : case 'F':
596 [ + - ]: 7 : g_ctrlr_path = strdup(optarg);
597 [ + - ]: 7 : if (!g_ctrlr_path) {
598 : 0 : fprintf(stderr, "cannot strdup: %s\n", optarg);
599 : 0 : return -ENOMEM;
600 : : }
601 : 7 : break;
602 : : case 'N':
603 : 0 : repro_file = fopen(optarg, "r");
604 [ # # ]: 0 : if (repro_file == NULL) {
605 [ # # ]: 0 : fprintf(stderr, "could not open %s: %s\n", optarg, spdk_strerror(errno));
606 : 0 : return -1;
607 : : }
608 : 0 : g_repro_data = spdk_posix_file_load(repro_file, &g_repro_size);
609 [ # # ]: 0 : if (g_repro_data == NULL) {
610 : 0 : fprintf(stderr, "could not load data for file %s\n", optarg);
611 : 0 : return -1;
612 : : }
613 : 0 : break;
614 : : case 'P':
615 [ + - ]: 7 : g_artifact_prefix = strdup(optarg);
616 [ + - ]: 7 : if (!g_artifact_prefix) {
617 : 0 : fprintf(stderr, "cannot strdup: %s\n", optarg);
618 : 0 : return -ENOMEM;
619 : : }
620 : 7 : break;
621 : : case 'Y':
622 [ + - + - ]: 7 : g_io_thread.io_ctrlr_path = strdup(optarg);
623 [ + - + - ]: 7 : if (!g_io_thread.io_ctrlr_path) {
624 : 0 : fprintf(stderr, "cannot strdup: %s\n", optarg);
625 : 0 : return -ENOMEM;
626 : : }
627 : 7 : break;
628 : : case 't':
629 : : case 'Z':
630 : 14 : tmp = spdk_strtoll(optarg, 10);
631 [ + - - + ]: 14 : if (tmp < 0 || tmp >= INT_MAX) {
632 : 0 : fprintf(stderr, "Invalid value '%s' for option -%c.\n", optarg, ch);
633 : 0 : return -EINVAL;
634 : : }
635 [ - + + ]: 14 : switch (ch) {
636 : : case 't':
637 : 7 : g_time_in_sec = tmp;
638 : 7 : break;
639 : : case 'Z':
640 [ - + ]: 7 : if ((unsigned long)tmp >= NUM_FUZZERS) {
641 : 0 : fprintf(stderr, "Invalid fuzz type %lld (max %lu)\n", tmp, NUM_FUZZERS - 1);
642 : 0 : return -EINVAL;
643 : : }
644 [ - + - + ]: 7 : g_fuzzer = &g_fuzzers[tmp];
645 : 7 : break;
646 : : }
647 : 14 : break;
648 : 0 : case '?':
649 : : default:
650 : 0 : return -EINVAL;
651 : : }
652 : 42 : return 0;
653 : 42 : }
654 : :
655 : : static void
656 : 0 : fuzz_shutdown(void)
657 : : {
658 : : /* If the user terminates the fuzzer prematurely, it is likely due
659 : : * to an input hang. So raise a SIGSEGV signal which will cause the
660 : : * fuzzer to generate a crash file for the last input.
661 : : *
662 : : * Note that the fuzzer will always generate a crash file, even if
663 : : * we get our TestOneInput() function (which is called by the fuzzer)
664 : : * to pthread_exit(). So just doing the SIGSEGV here in all cases is
665 : : * simpler than trying to differentiate between hung inputs and
666 : : * an impatient user.
667 : : */
668 : 0 : pthread_kill(g_fuzz_td, SIGSEGV);
669 : 0 : }
670 : :
671 : : int
672 : 7 : main(int argc, char **argv)
673 : : {
674 : 7 : struct spdk_app_opts opts = {};
675 : 7 : int rc = 0;
676 : :
677 : 7 : spdk_app_opts_init(&opts, sizeof(opts));
678 : 7 : opts.name = "vfio_fuzz";
679 : 7 : opts.shutdown_cb = fuzz_shutdown;
680 : :
681 [ - + - + ]: 14 : if ((rc = spdk_app_parse_args(argc, argv, &opts, "D:F:N:P:t:Y:Z:", NULL, vfio_fuzz_parse,
682 : 7 : vfio_fuzz_usage) != SPDK_APP_PARSE_ARGS_SUCCESS)) {
683 : 0 : return rc;
684 : : }
685 : :
686 [ + - ]: 7 : if (!g_corpus_dir) {
687 : 0 : fprintf(stderr, "Must specify corpus dir with -D option\n");
688 : 0 : return -1;
689 : : }
690 : :
691 [ + - ]: 7 : if (!g_ctrlr_path) {
692 : 0 : fprintf(stderr, "Must specify ctrlr path with -F option\n");
693 : 0 : return -1;
694 : : }
695 : :
696 [ - + ]: 7 : if (!g_fuzzer) {
697 : 0 : fprintf(stderr, "Must specify fuzzer with -Z option\n");
698 : 0 : return -1;
699 : : }
700 : :
701 : 7 : rc = spdk_app_start(&opts, begin_fuzz, NULL);
702 : :
703 : 7 : spdk_app_fini();
704 : 7 : return rc;
705 : 7 : }
|