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