cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

virtio_blk_fuzz.c (6776B)


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