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 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 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 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 149472a07a6SAlexander Bulekov static void virtio_scsi_pre_fuzz(QTestState *s) 150472a07a6SAlexander Bulekov { 151472a07a6SAlexander Bulekov qos_init_path(s); 152472a07a6SAlexander Bulekov } 153472a07a6SAlexander Bulekov 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 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