xref: /qemu/tests/qtest/fuzz/virtio_scsi_fuzz.c (revision 907b5105f1b9e1af1abbdbb4f2039c7ab105c001)
1472a07a6SAlexander Bulekov /*
2472a07a6SAlexander Bulekov  * virtio-serial Fuzzing Target
3472a07a6SAlexander Bulekov  *
4472a07a6SAlexander Bulekov  * Copyright Red Hat Inc., 2019
5472a07a6SAlexander Bulekov  *
6472a07a6SAlexander Bulekov  * Authors:
7472a07a6SAlexander Bulekov  *  Alexander Bulekov   <alxndr@bu.edu>
8472a07a6SAlexander Bulekov  *
9472a07a6SAlexander Bulekov  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10472a07a6SAlexander Bulekov  * See the COPYING file in the top-level directory.
11472a07a6SAlexander Bulekov  */
12472a07a6SAlexander Bulekov 
13472a07a6SAlexander Bulekov #include "qemu/osdep.h"
14472a07a6SAlexander Bulekov 
15*907b5105SMarc-André Lureau #include "tests/qtest/libqtest.h"
1664ed6f92SPaolo Bonzini #include "tests/qtest/libqos/virtio-scsi.h"
1764ed6f92SPaolo Bonzini #include "tests/qtest/libqos/virtio.h"
1864ed6f92SPaolo Bonzini #include "tests/qtest/libqos/virtio-pci.h"
19472a07a6SAlexander Bulekov #include "standard-headers/linux/virtio_ids.h"
20472a07a6SAlexander Bulekov #include "standard-headers/linux/virtio_pci.h"
21472a07a6SAlexander Bulekov #include "standard-headers/linux/virtio_scsi.h"
22472a07a6SAlexander Bulekov #include "fuzz.h"
23472a07a6SAlexander Bulekov #include "fork_fuzz.h"
24472a07a6SAlexander Bulekov #include "qos_fuzz.h"
25472a07a6SAlexander Bulekov 
26472a07a6SAlexander Bulekov #define PCI_SLOT                0x02
27472a07a6SAlexander Bulekov #define PCI_FN                  0x00
28472a07a6SAlexander Bulekov #define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
29472a07a6SAlexander Bulekov 
30472a07a6SAlexander Bulekov #define MAX_NUM_QUEUES 64
31472a07a6SAlexander Bulekov 
32472a07a6SAlexander Bulekov /* Based on tests/virtio-scsi-test.c */
33472a07a6SAlexander Bulekov typedef struct {
34472a07a6SAlexander Bulekov     int num_queues;
35472a07a6SAlexander Bulekov     QVirtQueue *vq[MAX_NUM_QUEUES + 2];
36472a07a6SAlexander Bulekov } QVirtioSCSIQueues;
37472a07a6SAlexander Bulekov 
38472a07a6SAlexander Bulekov static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev, uint64_t mask)
39472a07a6SAlexander Bulekov {
40472a07a6SAlexander Bulekov     QVirtioSCSIQueues *vs;
41472a07a6SAlexander Bulekov     uint64_t feat;
42472a07a6SAlexander Bulekov     int i;
43472a07a6SAlexander Bulekov 
44472a07a6SAlexander Bulekov     vs = g_new0(QVirtioSCSIQueues, 1);
45472a07a6SAlexander Bulekov 
46472a07a6SAlexander Bulekov     feat = qvirtio_get_features(dev);
47472a07a6SAlexander Bulekov     if (mask) {
48472a07a6SAlexander Bulekov         feat &= ~QVIRTIO_F_BAD_FEATURE | mask;
49472a07a6SAlexander Bulekov     } else {
50472a07a6SAlexander Bulekov         feat &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX));
51472a07a6SAlexander Bulekov     }
52472a07a6SAlexander Bulekov     qvirtio_set_features(dev, feat);
53472a07a6SAlexander Bulekov 
54472a07a6SAlexander Bulekov     vs->num_queues = qvirtio_config_readl(dev, 0);
55472a07a6SAlexander Bulekov 
56472a07a6SAlexander Bulekov     for (i = 0; i < vs->num_queues + 2; i++) {
57472a07a6SAlexander Bulekov         vs->vq[i] = qvirtqueue_setup(dev, fuzz_qos_alloc, i);
58472a07a6SAlexander Bulekov     }
59472a07a6SAlexander Bulekov 
60472a07a6SAlexander Bulekov     qvirtio_set_driver_ok(dev);
61472a07a6SAlexander Bulekov 
62472a07a6SAlexander Bulekov     return vs;
63472a07a6SAlexander Bulekov }
64472a07a6SAlexander Bulekov 
65472a07a6SAlexander Bulekov static void virtio_scsi_fuzz(QTestState *s, QVirtioSCSIQueues* queues,
66472a07a6SAlexander Bulekov         const unsigned char *Data, size_t Size)
67472a07a6SAlexander Bulekov {
68472a07a6SAlexander Bulekov     /*
69472a07a6SAlexander Bulekov      * Data is a sequence of random bytes. We split them up into "actions",
70472a07a6SAlexander Bulekov      * followed by data:
71472a07a6SAlexander Bulekov      * [vqa][dddddddd][vqa][dddd][vqa][dddddddddddd] ...
72472a07a6SAlexander Bulekov      * The length of the data is specified by the preceding vqa.length
73472a07a6SAlexander Bulekov      */
74472a07a6SAlexander Bulekov     typedef struct vq_action {
75472a07a6SAlexander Bulekov         uint8_t queue;
76472a07a6SAlexander Bulekov         uint8_t length;
77472a07a6SAlexander Bulekov         uint8_t write;
78472a07a6SAlexander Bulekov         uint8_t next;
79472a07a6SAlexander Bulekov         uint8_t kick;
80472a07a6SAlexander Bulekov     } vq_action;
81472a07a6SAlexander Bulekov 
82472a07a6SAlexander Bulekov     /* Keep track of the free head for each queue we interact with */
83472a07a6SAlexander Bulekov     bool vq_touched[MAX_NUM_QUEUES + 2] = {0};
84472a07a6SAlexander Bulekov     uint32_t free_head[MAX_NUM_QUEUES + 2];
85472a07a6SAlexander Bulekov 
86472a07a6SAlexander Bulekov     QGuestAllocator *t_alloc = fuzz_qos_alloc;
87472a07a6SAlexander Bulekov 
88472a07a6SAlexander Bulekov     QVirtioSCSI *scsi = fuzz_qos_obj;
89472a07a6SAlexander Bulekov     QVirtioDevice *dev = scsi->vdev;
90472a07a6SAlexander Bulekov     QVirtQueue *q;
91472a07a6SAlexander Bulekov     vq_action vqa;
92472a07a6SAlexander Bulekov     while (Size >= sizeof(vqa)) {
93472a07a6SAlexander Bulekov         /* Copy the action, so we can normalize length, queue and flags */
94472a07a6SAlexander Bulekov         memcpy(&vqa, Data, sizeof(vqa));
95472a07a6SAlexander Bulekov 
96472a07a6SAlexander Bulekov         Data += sizeof(vqa);
97472a07a6SAlexander Bulekov         Size -= sizeof(vqa);
98472a07a6SAlexander Bulekov 
99472a07a6SAlexander Bulekov         vqa.queue = vqa.queue % queues->num_queues;
100472a07a6SAlexander Bulekov         /* Cap length at the number of remaining bytes in data */
101472a07a6SAlexander Bulekov         vqa.length = vqa.length >= Size ? Size : vqa.length;
102472a07a6SAlexander Bulekov         vqa.write = vqa.write & 1;
103472a07a6SAlexander Bulekov         vqa.next = vqa.next & 1;
104472a07a6SAlexander Bulekov         vqa.kick = vqa.kick & 1;
105472a07a6SAlexander Bulekov 
106472a07a6SAlexander Bulekov 
107472a07a6SAlexander Bulekov         q = queues->vq[vqa.queue];
108472a07a6SAlexander Bulekov 
109472a07a6SAlexander Bulekov         /* Copy the data into ram, and place it on the virtqueue */
110472a07a6SAlexander Bulekov         uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
111472a07a6SAlexander Bulekov         qtest_memwrite(s, req_addr, Data, vqa.length);
112472a07a6SAlexander Bulekov         if (vq_touched[vqa.queue] == 0) {
113472a07a6SAlexander Bulekov             vq_touched[vqa.queue] = 1;
114472a07a6SAlexander Bulekov             free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length,
115472a07a6SAlexander Bulekov                     vqa.write, vqa.next);
116472a07a6SAlexander Bulekov         } else {
117472a07a6SAlexander Bulekov             qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
118472a07a6SAlexander Bulekov         }
119472a07a6SAlexander Bulekov 
120472a07a6SAlexander Bulekov         if (vqa.kick) {
121472a07a6SAlexander Bulekov             qvirtqueue_kick(s, dev, q, free_head[vqa.queue]);
122472a07a6SAlexander Bulekov             free_head[vqa.queue] = 0;
123472a07a6SAlexander Bulekov         }
124472a07a6SAlexander Bulekov         Data += vqa.length;
125472a07a6SAlexander Bulekov         Size -= vqa.length;
126472a07a6SAlexander Bulekov     }
127472a07a6SAlexander Bulekov     /* In the end, kick each queue we interacted with */
128472a07a6SAlexander Bulekov     for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
129472a07a6SAlexander Bulekov         if (vq_touched[i]) {
130472a07a6SAlexander Bulekov             qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
131472a07a6SAlexander Bulekov         }
132472a07a6SAlexander Bulekov     }
133472a07a6SAlexander Bulekov }
134472a07a6SAlexander Bulekov 
135472a07a6SAlexander Bulekov static void virtio_scsi_fork_fuzz(QTestState *s,
136472a07a6SAlexander Bulekov         const unsigned char *Data, size_t Size)
137472a07a6SAlexander Bulekov {
138472a07a6SAlexander Bulekov     QVirtioSCSI *scsi = fuzz_qos_obj;
139472a07a6SAlexander Bulekov     static QVirtioSCSIQueues *queues;
140472a07a6SAlexander Bulekov     if (!queues) {
141472a07a6SAlexander Bulekov         queues = qvirtio_scsi_init(scsi->vdev, 0);
142472a07a6SAlexander Bulekov     }
143472a07a6SAlexander Bulekov     if (fork() == 0) {
144472a07a6SAlexander Bulekov         virtio_scsi_fuzz(s, queues, Data, Size);
145472a07a6SAlexander Bulekov         flush_events(s);
146472a07a6SAlexander Bulekov         _Exit(0);
147472a07a6SAlexander Bulekov     } else {
148dfd5ddb5SAlexander Bulekov         flush_events(s);
149472a07a6SAlexander Bulekov         wait(NULL);
150472a07a6SAlexander Bulekov     }
151472a07a6SAlexander Bulekov }
152472a07a6SAlexander Bulekov 
153472a07a6SAlexander Bulekov static void virtio_scsi_with_flag_fuzz(QTestState *s,
154472a07a6SAlexander Bulekov         const unsigned char *Data, size_t Size)
155472a07a6SAlexander Bulekov {
156472a07a6SAlexander Bulekov     QVirtioSCSI *scsi = fuzz_qos_obj;
157472a07a6SAlexander Bulekov     static QVirtioSCSIQueues *queues;
158472a07a6SAlexander Bulekov 
159472a07a6SAlexander Bulekov     if (fork() == 0) {
160472a07a6SAlexander Bulekov         if (Size >= sizeof(uint64_t)) {
161472a07a6SAlexander Bulekov             queues = qvirtio_scsi_init(scsi->vdev, *(uint64_t *)Data);
162472a07a6SAlexander Bulekov             virtio_scsi_fuzz(s, queues,
163472a07a6SAlexander Bulekov                              Data + sizeof(uint64_t), Size - sizeof(uint64_t));
164472a07a6SAlexander Bulekov             flush_events(s);
165472a07a6SAlexander Bulekov         }
166472a07a6SAlexander Bulekov         _Exit(0);
167472a07a6SAlexander Bulekov     } else {
168dfd5ddb5SAlexander Bulekov         flush_events(s);
169472a07a6SAlexander Bulekov         wait(NULL);
170472a07a6SAlexander Bulekov     }
171472a07a6SAlexander Bulekov }
172472a07a6SAlexander Bulekov 
173472a07a6SAlexander Bulekov static void virtio_scsi_pre_fuzz(QTestState *s)
174472a07a6SAlexander Bulekov {
175472a07a6SAlexander Bulekov     qos_init_path(s);
176472a07a6SAlexander Bulekov     counter_shm_init();
177472a07a6SAlexander Bulekov }
178472a07a6SAlexander Bulekov 
179472a07a6SAlexander Bulekov static void *virtio_scsi_test_setup(GString *cmd_line, void *arg)
180472a07a6SAlexander Bulekov {
181472a07a6SAlexander Bulekov     g_string_append(cmd_line,
182472a07a6SAlexander Bulekov                     " -drive file=blkdebug::null-co://,"
183472a07a6SAlexander Bulekov                     "file.image.read-zeroes=on,"
184472a07a6SAlexander Bulekov                     "if=none,id=dr1,format=raw,file.align=4k "
185472a07a6SAlexander Bulekov                     "-device scsi-hd,drive=dr1,lun=0,scsi-id=1");
186472a07a6SAlexander Bulekov     return arg;
187472a07a6SAlexander Bulekov }
188472a07a6SAlexander Bulekov 
189472a07a6SAlexander Bulekov 
190472a07a6SAlexander Bulekov static void register_virtio_scsi_fuzz_targets(void)
191472a07a6SAlexander Bulekov {
192472a07a6SAlexander Bulekov     fuzz_add_qos_target(&(FuzzTarget){
193472a07a6SAlexander Bulekov                 .name = "virtio-scsi-fuzz",
194472a07a6SAlexander Bulekov                 .description = "Fuzz the virtio-scsi virtual queues, forking "
195472a07a6SAlexander Bulekov                                 "for each fuzz run",
196472a07a6SAlexander Bulekov                 .pre_vm_init = &counter_shm_init,
197472a07a6SAlexander Bulekov                 .pre_fuzz = &virtio_scsi_pre_fuzz,
198472a07a6SAlexander Bulekov                 .fuzz = virtio_scsi_fork_fuzz,},
199472a07a6SAlexander Bulekov                 "virtio-scsi",
200472a07a6SAlexander Bulekov                 &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
201472a07a6SAlexander Bulekov                 );
202472a07a6SAlexander Bulekov 
203472a07a6SAlexander Bulekov     fuzz_add_qos_target(&(FuzzTarget){
204472a07a6SAlexander Bulekov                 .name = "virtio-scsi-flags-fuzz",
205472a07a6SAlexander Bulekov                 .description = "Fuzz the virtio-scsi virtual queues, forking "
206472a07a6SAlexander Bulekov                 "for each fuzz run (also fuzzes the virtio flags)",
207472a07a6SAlexander Bulekov                 .pre_vm_init = &counter_shm_init,
208472a07a6SAlexander Bulekov                 .pre_fuzz = &virtio_scsi_pre_fuzz,
209472a07a6SAlexander Bulekov                 .fuzz = virtio_scsi_with_flag_fuzz,},
210472a07a6SAlexander Bulekov                 "virtio-scsi",
211472a07a6SAlexander Bulekov                 &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
212472a07a6SAlexander Bulekov                 );
213472a07a6SAlexander Bulekov }
214472a07a6SAlexander Bulekov 
215472a07a6SAlexander Bulekov fuzz_target_init(register_virtio_scsi_fuzz_targets);
216