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