xref: /openbmc/qemu/tests/qtest/fuzz/virtio_blk_fuzz.c (revision d4e279141bf59e702beae3a1002b482f733a2ac2)
1*d4e27914SDima Stepanov /*
2*d4e27914SDima Stepanov  * virtio-blk Fuzzing Target
3*d4e27914SDima Stepanov  *
4*d4e27914SDima Stepanov  * Copyright Red Hat Inc., 2020
5*d4e27914SDima Stepanov  *
6*d4e27914SDima Stepanov  * Based on virtio-scsi-fuzz target.
7*d4e27914SDima Stepanov  *
8*d4e27914SDima Stepanov  * This work is licensed under the terms of the GNU GPL, version 2 or later.
9*d4e27914SDima Stepanov  * See the COPYING file in the top-level directory.
10*d4e27914SDima Stepanov  */
11*d4e27914SDima Stepanov 
12*d4e27914SDima Stepanov #include "qemu/osdep.h"
13*d4e27914SDima Stepanov 
14*d4e27914SDima Stepanov #include "tests/qtest/libqos/libqtest.h"
15*d4e27914SDima Stepanov #include "tests/qtest/libqos/virtio-blk.h"
16*d4e27914SDima Stepanov #include "tests/qtest/libqos/virtio.h"
17*d4e27914SDima Stepanov #include "tests/qtest/libqos/virtio-pci.h"
18*d4e27914SDima Stepanov #include "standard-headers/linux/virtio_ids.h"
19*d4e27914SDima Stepanov #include "standard-headers/linux/virtio_pci.h"
20*d4e27914SDima Stepanov #include "standard-headers/linux/virtio_blk.h"
21*d4e27914SDima Stepanov #include "fuzz.h"
22*d4e27914SDima Stepanov #include "fork_fuzz.h"
23*d4e27914SDima Stepanov #include "qos_fuzz.h"
24*d4e27914SDima Stepanov 
25*d4e27914SDima Stepanov #define TEST_IMAGE_SIZE         (64 * 1024 * 1024)
26*d4e27914SDima Stepanov #define PCI_SLOT                0x02
27*d4e27914SDima Stepanov #define PCI_FN                  0x00
28*d4e27914SDima Stepanov 
29*d4e27914SDima Stepanov #define MAX_NUM_QUEUES 64
30*d4e27914SDima Stepanov 
31*d4e27914SDima Stepanov /* Based on tests/qtest/virtio-blk-test.c. */
32*d4e27914SDima Stepanov typedef struct {
33*d4e27914SDima Stepanov     int num_queues;
34*d4e27914SDima Stepanov     QVirtQueue *vq[MAX_NUM_QUEUES + 2];
35*d4e27914SDima Stepanov } QVirtioBlkQueues;
36*d4e27914SDima Stepanov 
37*d4e27914SDima Stepanov static QVirtioBlkQueues *qvirtio_blk_init(QVirtioDevice *dev, uint64_t mask)
38*d4e27914SDima Stepanov {
39*d4e27914SDima Stepanov     QVirtioBlkQueues *vs;
40*d4e27914SDima Stepanov     uint64_t features;
41*d4e27914SDima Stepanov 
42*d4e27914SDima Stepanov     vs = g_new0(QVirtioBlkQueues, 1);
43*d4e27914SDima Stepanov 
44*d4e27914SDima Stepanov     features = qvirtio_get_features(dev);
45*d4e27914SDima Stepanov     if (!mask) {
46*d4e27914SDima Stepanov         mask = ~((1u << VIRTIO_RING_F_INDIRECT_DESC) |
47*d4e27914SDima Stepanov                 (1u << VIRTIO_RING_F_EVENT_IDX) |
48*d4e27914SDima Stepanov                 (1u << VIRTIO_BLK_F_SCSI));
49*d4e27914SDima Stepanov     }
50*d4e27914SDima Stepanov     mask |= ~QVIRTIO_F_BAD_FEATURE;
51*d4e27914SDima Stepanov     features &= mask;
52*d4e27914SDima Stepanov     qvirtio_set_features(dev, features);
53*d4e27914SDima Stepanov 
54*d4e27914SDima Stepanov     vs->num_queues = 1;
55*d4e27914SDima Stepanov     vs->vq[0] = qvirtqueue_setup(dev, fuzz_qos_alloc, 0);
56*d4e27914SDima Stepanov 
57*d4e27914SDima Stepanov     qvirtio_set_driver_ok(dev);
58*d4e27914SDima Stepanov 
59*d4e27914SDima Stepanov     return vs;
60*d4e27914SDima Stepanov }
61*d4e27914SDima Stepanov 
62*d4e27914SDima Stepanov static void virtio_blk_fuzz(QTestState *s, QVirtioBlkQueues* queues,
63*d4e27914SDima Stepanov         const unsigned char *Data, size_t Size)
64*d4e27914SDima Stepanov {
65*d4e27914SDima Stepanov     /*
66*d4e27914SDima Stepanov      * Data is a sequence of random bytes. We split them up into "actions",
67*d4e27914SDima Stepanov      * followed by data:
68*d4e27914SDima Stepanov      * [vqa][dddddddd][vqa][dddd][vqa][dddddddddddd] ...
69*d4e27914SDima Stepanov      * The length of the data is specified by the preceding vqa.length
70*d4e27914SDima Stepanov      */
71*d4e27914SDima Stepanov     typedef struct vq_action {
72*d4e27914SDima Stepanov         uint8_t queue;
73*d4e27914SDima Stepanov         uint8_t length;
74*d4e27914SDima Stepanov         uint8_t write;
75*d4e27914SDima Stepanov         uint8_t next;
76*d4e27914SDima Stepanov         uint8_t kick;
77*d4e27914SDima Stepanov     } vq_action;
78*d4e27914SDima Stepanov 
79*d4e27914SDima Stepanov     /* Keep track of the free head for each queue we interact with */
80*d4e27914SDima Stepanov     bool vq_touched[MAX_NUM_QUEUES + 2] = {0};
81*d4e27914SDima Stepanov     uint32_t free_head[MAX_NUM_QUEUES + 2];
82*d4e27914SDima Stepanov 
83*d4e27914SDima Stepanov     QGuestAllocator *t_alloc = fuzz_qos_alloc;
84*d4e27914SDima Stepanov 
85*d4e27914SDima Stepanov     QVirtioBlk *blk = fuzz_qos_obj;
86*d4e27914SDima Stepanov     QVirtioDevice *dev = blk->vdev;
87*d4e27914SDima Stepanov     QVirtQueue *q;
88*d4e27914SDima Stepanov     vq_action vqa;
89*d4e27914SDima Stepanov     while (Size >= sizeof(vqa)) {
90*d4e27914SDima Stepanov         /* Copy the action, so we can normalize length, queue and flags */
91*d4e27914SDima Stepanov         memcpy(&vqa, Data, sizeof(vqa));
92*d4e27914SDima Stepanov 
93*d4e27914SDima Stepanov         Data += sizeof(vqa);
94*d4e27914SDima Stepanov         Size -= sizeof(vqa);
95*d4e27914SDima Stepanov 
96*d4e27914SDima Stepanov         vqa.queue = vqa.queue % queues->num_queues;
97*d4e27914SDima Stepanov         /* Cap length at the number of remaining bytes in data */
98*d4e27914SDima Stepanov         vqa.length = vqa.length >= Size ? Size : vqa.length;
99*d4e27914SDima Stepanov         vqa.write = vqa.write & 1;
100*d4e27914SDima Stepanov         vqa.next = vqa.next & 1;
101*d4e27914SDima Stepanov         vqa.kick = vqa.kick & 1;
102*d4e27914SDima Stepanov 
103*d4e27914SDima Stepanov         q = queues->vq[vqa.queue];
104*d4e27914SDima Stepanov 
105*d4e27914SDima Stepanov         /* Copy the data into ram, and place it on the virtqueue */
106*d4e27914SDima Stepanov         uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
107*d4e27914SDima Stepanov         qtest_memwrite(s, req_addr, Data, vqa.length);
108*d4e27914SDima Stepanov         if (vq_touched[vqa.queue] == 0) {
109*d4e27914SDima Stepanov             vq_touched[vqa.queue] = 1;
110*d4e27914SDima Stepanov             free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length,
111*d4e27914SDima Stepanov                     vqa.write, vqa.next);
112*d4e27914SDima Stepanov         } else {
113*d4e27914SDima Stepanov             qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
114*d4e27914SDima Stepanov         }
115*d4e27914SDima Stepanov 
116*d4e27914SDima Stepanov         if (vqa.kick) {
117*d4e27914SDima Stepanov             qvirtqueue_kick(s, dev, q, free_head[vqa.queue]);
118*d4e27914SDima Stepanov             free_head[vqa.queue] = 0;
119*d4e27914SDima Stepanov         }
120*d4e27914SDima Stepanov         Data += vqa.length;
121*d4e27914SDima Stepanov         Size -= vqa.length;
122*d4e27914SDima Stepanov     }
123*d4e27914SDima Stepanov     /* In the end, kick each queue we interacted with */
124*d4e27914SDima Stepanov     for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
125*d4e27914SDima Stepanov         if (vq_touched[i]) {
126*d4e27914SDima Stepanov             qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
127*d4e27914SDima Stepanov         }
128*d4e27914SDima Stepanov     }
129*d4e27914SDima Stepanov }
130*d4e27914SDima Stepanov 
131*d4e27914SDima Stepanov static void virtio_blk_fork_fuzz(QTestState *s,
132*d4e27914SDima Stepanov         const unsigned char *Data, size_t Size)
133*d4e27914SDima Stepanov {
134*d4e27914SDima Stepanov     QVirtioBlk *blk = fuzz_qos_obj;
135*d4e27914SDima Stepanov     static QVirtioBlkQueues *queues;
136*d4e27914SDima Stepanov     if (!queues) {
137*d4e27914SDima Stepanov         queues = qvirtio_blk_init(blk->vdev, 0);
138*d4e27914SDima Stepanov     }
139*d4e27914SDima Stepanov     if (fork() == 0) {
140*d4e27914SDima Stepanov         virtio_blk_fuzz(s, queues, Data, Size);
141*d4e27914SDima Stepanov         flush_events(s);
142*d4e27914SDima Stepanov         _Exit(0);
143*d4e27914SDima Stepanov     } else {
144*d4e27914SDima Stepanov         flush_events(s);
145*d4e27914SDima Stepanov         wait(NULL);
146*d4e27914SDima Stepanov     }
147*d4e27914SDima Stepanov }
148*d4e27914SDima Stepanov 
149*d4e27914SDima Stepanov static void virtio_blk_with_flag_fuzz(QTestState *s,
150*d4e27914SDima Stepanov         const unsigned char *Data, size_t Size)
151*d4e27914SDima Stepanov {
152*d4e27914SDima Stepanov     QVirtioBlk *blk = fuzz_qos_obj;
153*d4e27914SDima Stepanov     static QVirtioBlkQueues *queues;
154*d4e27914SDima Stepanov 
155*d4e27914SDima Stepanov     if (fork() == 0) {
156*d4e27914SDima Stepanov         if (Size >= sizeof(uint64_t)) {
157*d4e27914SDima Stepanov             queues = qvirtio_blk_init(blk->vdev, *(uint64_t *)Data);
158*d4e27914SDima Stepanov             virtio_blk_fuzz(s, queues,
159*d4e27914SDima Stepanov                              Data + sizeof(uint64_t), Size - sizeof(uint64_t));
160*d4e27914SDima Stepanov             flush_events(s);
161*d4e27914SDima Stepanov         }
162*d4e27914SDima Stepanov         _Exit(0);
163*d4e27914SDima Stepanov     } else {
164*d4e27914SDima Stepanov         flush_events(s);
165*d4e27914SDima Stepanov         wait(NULL);
166*d4e27914SDima Stepanov     }
167*d4e27914SDima Stepanov }
168*d4e27914SDima Stepanov 
169*d4e27914SDima Stepanov static void virtio_blk_pre_fuzz(QTestState *s)
170*d4e27914SDima Stepanov {
171*d4e27914SDima Stepanov     qos_init_path(s);
172*d4e27914SDima Stepanov     counter_shm_init();
173*d4e27914SDima Stepanov }
174*d4e27914SDima Stepanov 
175*d4e27914SDima Stepanov static void drive_destroy(void *path)
176*d4e27914SDima Stepanov {
177*d4e27914SDima Stepanov     unlink(path);
178*d4e27914SDima Stepanov     g_free(path);
179*d4e27914SDima Stepanov }
180*d4e27914SDima Stepanov 
181*d4e27914SDima Stepanov static char *drive_create(void)
182*d4e27914SDima Stepanov {
183*d4e27914SDima Stepanov     int fd, ret;
184*d4e27914SDima Stepanov     char *t_path = g_strdup("/tmp/qtest.XXXXXX");
185*d4e27914SDima Stepanov 
186*d4e27914SDima Stepanov     /* Create a temporary raw image */
187*d4e27914SDima Stepanov     fd = mkstemp(t_path);
188*d4e27914SDima Stepanov     g_assert_cmpint(fd, >=, 0);
189*d4e27914SDima Stepanov     ret = ftruncate(fd, TEST_IMAGE_SIZE);
190*d4e27914SDima Stepanov     g_assert_cmpint(ret, ==, 0);
191*d4e27914SDima Stepanov     close(fd);
192*d4e27914SDima Stepanov 
193*d4e27914SDima Stepanov     g_test_queue_destroy(drive_destroy, t_path);
194*d4e27914SDima Stepanov     return t_path;
195*d4e27914SDima Stepanov }
196*d4e27914SDima Stepanov 
197*d4e27914SDima Stepanov static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
198*d4e27914SDima Stepanov {
199*d4e27914SDima Stepanov     char *tmp_path = drive_create();
200*d4e27914SDima Stepanov 
201*d4e27914SDima Stepanov     g_string_append_printf(cmd_line,
202*d4e27914SDima Stepanov                            " -drive if=none,id=drive0,file=%s,"
203*d4e27914SDima Stepanov                            "format=raw,auto-read-only=off ",
204*d4e27914SDima Stepanov                            tmp_path);
205*d4e27914SDima Stepanov 
206*d4e27914SDima Stepanov     return arg;
207*d4e27914SDima Stepanov }
208*d4e27914SDima Stepanov 
209*d4e27914SDima Stepanov static void register_virtio_blk_fuzz_targets(void)
210*d4e27914SDima Stepanov {
211*d4e27914SDima Stepanov     fuzz_add_qos_target(&(FuzzTarget){
212*d4e27914SDima Stepanov                 .name = "virtio-blk-fuzz",
213*d4e27914SDima Stepanov                 .description = "Fuzz the virtio-blk virtual queues, forking "
214*d4e27914SDima Stepanov                                 "for each fuzz run",
215*d4e27914SDima Stepanov                 .pre_vm_init = &counter_shm_init,
216*d4e27914SDima Stepanov                 .pre_fuzz = &virtio_blk_pre_fuzz,
217*d4e27914SDima Stepanov                 .fuzz = virtio_blk_fork_fuzz,},
218*d4e27914SDima Stepanov                 "virtio-blk",
219*d4e27914SDima Stepanov                 &(QOSGraphTestOptions){.before = virtio_blk_test_setup}
220*d4e27914SDima Stepanov                 );
221*d4e27914SDima Stepanov 
222*d4e27914SDima Stepanov     fuzz_add_qos_target(&(FuzzTarget){
223*d4e27914SDima Stepanov                 .name = "virtio-blk-flags-fuzz",
224*d4e27914SDima Stepanov                 .description = "Fuzz the virtio-blk virtual queues, forking "
225*d4e27914SDima Stepanov                 "for each fuzz run (also fuzzes the virtio flags)",
226*d4e27914SDima Stepanov                 .pre_vm_init = &counter_shm_init,
227*d4e27914SDima Stepanov                 .pre_fuzz = &virtio_blk_pre_fuzz,
228*d4e27914SDima Stepanov                 .fuzz = virtio_blk_with_flag_fuzz,},
229*d4e27914SDima Stepanov                 "virtio-blk",
230*d4e27914SDima Stepanov                 &(QOSGraphTestOptions){.before = virtio_blk_test_setup}
231*d4e27914SDima Stepanov                 );
232*d4e27914SDima Stepanov }
233*d4e27914SDima Stepanov 
234*d4e27914SDima Stepanov fuzz_target_init(register_virtio_blk_fuzz_targets);
235