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