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
15907b5105SMarc-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 "qos_fuzz.h"
24472a07a6SAlexander Bulekov
25472a07a6SAlexander Bulekov #define PCI_SLOT 0x02
26472a07a6SAlexander Bulekov #define PCI_FN 0x00
27472a07a6SAlexander Bulekov #define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
28472a07a6SAlexander Bulekov
29472a07a6SAlexander Bulekov #define MAX_NUM_QUEUES 64
30472a07a6SAlexander Bulekov
31472a07a6SAlexander Bulekov /* Based on tests/virtio-scsi-test.c */
32472a07a6SAlexander Bulekov typedef struct {
33472a07a6SAlexander Bulekov int num_queues;
34472a07a6SAlexander Bulekov QVirtQueue *vq[MAX_NUM_QUEUES + 2];
35472a07a6SAlexander Bulekov } QVirtioSCSIQueues;
36472a07a6SAlexander Bulekov
qvirtio_scsi_init(QVirtioDevice * dev,uint64_t mask)37472a07a6SAlexander Bulekov static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev, uint64_t mask)
38472a07a6SAlexander Bulekov {
39472a07a6SAlexander Bulekov QVirtioSCSIQueues *vs;
40472a07a6SAlexander Bulekov uint64_t feat;
41472a07a6SAlexander Bulekov int i;
42472a07a6SAlexander Bulekov
43472a07a6SAlexander Bulekov vs = g_new0(QVirtioSCSIQueues, 1);
44472a07a6SAlexander Bulekov
45472a07a6SAlexander Bulekov feat = qvirtio_get_features(dev);
46472a07a6SAlexander Bulekov if (mask) {
47472a07a6SAlexander Bulekov feat &= ~QVIRTIO_F_BAD_FEATURE | mask;
48472a07a6SAlexander Bulekov } else {
49472a07a6SAlexander Bulekov feat &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX));
50472a07a6SAlexander Bulekov }
51472a07a6SAlexander Bulekov qvirtio_set_features(dev, feat);
52472a07a6SAlexander Bulekov
53472a07a6SAlexander Bulekov vs->num_queues = qvirtio_config_readl(dev, 0);
54472a07a6SAlexander Bulekov
55472a07a6SAlexander Bulekov for (i = 0; i < vs->num_queues + 2; i++) {
56472a07a6SAlexander Bulekov vs->vq[i] = qvirtqueue_setup(dev, fuzz_qos_alloc, i);
57472a07a6SAlexander Bulekov }
58472a07a6SAlexander Bulekov
59472a07a6SAlexander Bulekov qvirtio_set_driver_ok(dev);
60472a07a6SAlexander Bulekov
61472a07a6SAlexander Bulekov return vs;
62472a07a6SAlexander Bulekov }
63472a07a6SAlexander Bulekov
virtio_scsi_fuzz(QTestState * s,QVirtioSCSIQueues * queues,const unsigned char * Data,size_t Size)64472a07a6SAlexander Bulekov static void virtio_scsi_fuzz(QTestState *s, QVirtioSCSIQueues* queues,
65472a07a6SAlexander Bulekov const unsigned char *Data, size_t Size)
66472a07a6SAlexander Bulekov {
67472a07a6SAlexander Bulekov /*
68472a07a6SAlexander Bulekov * Data is a sequence of random bytes. We split them up into "actions",
69472a07a6SAlexander Bulekov * followed by data:
70472a07a6SAlexander Bulekov * [vqa][dddddddd][vqa][dddd][vqa][dddddddddddd] ...
71472a07a6SAlexander Bulekov * The length of the data is specified by the preceding vqa.length
72472a07a6SAlexander Bulekov */
73472a07a6SAlexander Bulekov typedef struct vq_action {
74472a07a6SAlexander Bulekov uint8_t queue;
75472a07a6SAlexander Bulekov uint8_t length;
76472a07a6SAlexander Bulekov uint8_t write;
77472a07a6SAlexander Bulekov uint8_t next;
78472a07a6SAlexander Bulekov uint8_t kick;
79472a07a6SAlexander Bulekov } vq_action;
80472a07a6SAlexander Bulekov
81472a07a6SAlexander Bulekov /* Keep track of the free head for each queue we interact with */
82472a07a6SAlexander Bulekov bool vq_touched[MAX_NUM_QUEUES + 2] = {0};
83472a07a6SAlexander Bulekov uint32_t free_head[MAX_NUM_QUEUES + 2];
84472a07a6SAlexander Bulekov
85472a07a6SAlexander Bulekov QGuestAllocator *t_alloc = fuzz_qos_alloc;
86472a07a6SAlexander Bulekov
87472a07a6SAlexander Bulekov QVirtioSCSI *scsi = fuzz_qos_obj;
88472a07a6SAlexander Bulekov QVirtioDevice *dev = scsi->vdev;
89472a07a6SAlexander Bulekov QVirtQueue *q;
90472a07a6SAlexander Bulekov vq_action vqa;
91472a07a6SAlexander Bulekov while (Size >= sizeof(vqa)) {
92472a07a6SAlexander Bulekov /* Copy the action, so we can normalize length, queue and flags */
93472a07a6SAlexander Bulekov memcpy(&vqa, Data, sizeof(vqa));
94472a07a6SAlexander Bulekov
95472a07a6SAlexander Bulekov Data += sizeof(vqa);
96472a07a6SAlexander Bulekov Size -= sizeof(vqa);
97472a07a6SAlexander Bulekov
98472a07a6SAlexander Bulekov vqa.queue = vqa.queue % queues->num_queues;
99472a07a6SAlexander Bulekov /* Cap length at the number of remaining bytes in data */
100472a07a6SAlexander Bulekov vqa.length = vqa.length >= Size ? Size : vqa.length;
101472a07a6SAlexander Bulekov vqa.write = vqa.write & 1;
102472a07a6SAlexander Bulekov vqa.next = vqa.next & 1;
103472a07a6SAlexander Bulekov vqa.kick = vqa.kick & 1;
104472a07a6SAlexander Bulekov
105472a07a6SAlexander Bulekov
106472a07a6SAlexander Bulekov q = queues->vq[vqa.queue];
107472a07a6SAlexander Bulekov
108472a07a6SAlexander Bulekov /* Copy the data into ram, and place it on the virtqueue */
109472a07a6SAlexander Bulekov uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
110472a07a6SAlexander Bulekov qtest_memwrite(s, req_addr, Data, vqa.length);
111472a07a6SAlexander Bulekov if (vq_touched[vqa.queue] == 0) {
112472a07a6SAlexander Bulekov vq_touched[vqa.queue] = 1;
113472a07a6SAlexander Bulekov free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length,
114472a07a6SAlexander Bulekov vqa.write, vqa.next);
115472a07a6SAlexander Bulekov } else {
116472a07a6SAlexander Bulekov qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
117472a07a6SAlexander Bulekov }
118472a07a6SAlexander Bulekov
119472a07a6SAlexander Bulekov if (vqa.kick) {
120472a07a6SAlexander Bulekov qvirtqueue_kick(s, dev, q, free_head[vqa.queue]);
121472a07a6SAlexander Bulekov free_head[vqa.queue] = 0;
122472a07a6SAlexander Bulekov }
123472a07a6SAlexander Bulekov Data += vqa.length;
124472a07a6SAlexander Bulekov Size -= vqa.length;
125472a07a6SAlexander Bulekov }
126472a07a6SAlexander Bulekov /* In the end, kick each queue we interacted with */
127472a07a6SAlexander Bulekov for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
128472a07a6SAlexander Bulekov if (vq_touched[i]) {
129472a07a6SAlexander Bulekov qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
130472a07a6SAlexander Bulekov }
131472a07a6SAlexander Bulekov }
132472a07a6SAlexander Bulekov }
133472a07a6SAlexander Bulekov
virtio_scsi_with_flag_fuzz(QTestState * s,const unsigned char * Data,size_t Size)134472a07a6SAlexander Bulekov static void virtio_scsi_with_flag_fuzz(QTestState *s,
135472a07a6SAlexander Bulekov const unsigned char *Data, size_t Size)
136472a07a6SAlexander Bulekov {
137472a07a6SAlexander Bulekov QVirtioSCSI *scsi = fuzz_qos_obj;
138472a07a6SAlexander Bulekov static QVirtioSCSIQueues *queues;
139472a07a6SAlexander Bulekov
140472a07a6SAlexander Bulekov if (Size >= sizeof(uint64_t)) {
141472a07a6SAlexander Bulekov queues = qvirtio_scsi_init(scsi->vdev, *(uint64_t *)Data);
142472a07a6SAlexander Bulekov virtio_scsi_fuzz(s, queues,
143472a07a6SAlexander Bulekov Data + sizeof(uint64_t), Size - sizeof(uint64_t));
144472a07a6SAlexander Bulekov flush_events(s);
145472a07a6SAlexander Bulekov }
146*5d3c73e2SAlexander Bulekov fuzz_reset(s);
147472a07a6SAlexander Bulekov }
148472a07a6SAlexander Bulekov
virtio_scsi_pre_fuzz(QTestState * s)149472a07a6SAlexander Bulekov static void virtio_scsi_pre_fuzz(QTestState *s)
150472a07a6SAlexander Bulekov {
151472a07a6SAlexander Bulekov qos_init_path(s);
152472a07a6SAlexander Bulekov }
153472a07a6SAlexander Bulekov
virtio_scsi_test_setup(GString * cmd_line,void * arg)154472a07a6SAlexander Bulekov static void *virtio_scsi_test_setup(GString *cmd_line, void *arg)
155472a07a6SAlexander Bulekov {
156472a07a6SAlexander Bulekov g_string_append(cmd_line,
157472a07a6SAlexander Bulekov " -drive file=blkdebug::null-co://,"
158472a07a6SAlexander Bulekov "file.image.read-zeroes=on,"
159472a07a6SAlexander Bulekov "if=none,id=dr1,format=raw,file.align=4k "
160472a07a6SAlexander Bulekov "-device scsi-hd,drive=dr1,lun=0,scsi-id=1");
161472a07a6SAlexander Bulekov return arg;
162472a07a6SAlexander Bulekov }
163472a07a6SAlexander Bulekov
164472a07a6SAlexander Bulekov
register_virtio_scsi_fuzz_targets(void)165472a07a6SAlexander Bulekov static void register_virtio_scsi_fuzz_targets(void)
166472a07a6SAlexander Bulekov {
167472a07a6SAlexander Bulekov fuzz_add_qos_target(&(FuzzTarget){
168472a07a6SAlexander Bulekov .name = "virtio-scsi-flags-fuzz",
169*5d3c73e2SAlexander Bulekov .description = "Fuzz the virtio-scsi virtual queues. "
170*5d3c73e2SAlexander Bulekov "Also fuzzes the virtio flags",
171472a07a6SAlexander Bulekov .pre_fuzz = &virtio_scsi_pre_fuzz,
172472a07a6SAlexander Bulekov .fuzz = virtio_scsi_with_flag_fuzz,},
173472a07a6SAlexander Bulekov "virtio-scsi",
174472a07a6SAlexander Bulekov &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
175472a07a6SAlexander Bulekov );
176472a07a6SAlexander Bulekov }
177472a07a6SAlexander Bulekov
178472a07a6SAlexander Bulekov fuzz_target_init(register_virtio_scsi_fuzz_targets);
179