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