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_net_fuzz.c (6310B)


      1/*
      2 * virtio-net Fuzzing Target
      3 *
      4 * Copyright Red Hat Inc., 2019
      5 *
      6 * Authors:
      7 *  Alexander Bulekov   <alxndr@bu.edu>
      8 *
      9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
     10 * See the COPYING file in the top-level directory.
     11 */
     12
     13#include "qemu/osdep.h"
     14
     15#include "standard-headers/linux/virtio_config.h"
     16#include "tests/qtest/libqos/libqtest.h"
     17#include "tests/qtest/libqos/virtio-net.h"
     18#include "fuzz.h"
     19#include "fork_fuzz.h"
     20#include "qos_fuzz.h"
     21
     22
     23#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
     24#define QVIRTIO_RX_VQ 0
     25#define QVIRTIO_TX_VQ 1
     26#define QVIRTIO_CTRL_VQ 2
     27
     28static int sockfds[2];
     29static bool sockfds_initialized;
     30
     31static void virtio_net_fuzz_multi(QTestState *s,
     32        const unsigned char *Data, size_t Size, bool check_used)
     33{
     34    typedef struct vq_action {
     35        uint8_t queue;
     36        uint8_t length;
     37        uint8_t write;
     38        uint8_t next;
     39        uint8_t rx;
     40    } vq_action;
     41
     42    uint32_t free_head = 0;
     43
     44    QGuestAllocator *t_alloc = fuzz_qos_alloc;
     45
     46    QVirtioNet *net_if = fuzz_qos_obj;
     47    QVirtioDevice *dev = net_if->vdev;
     48    QVirtQueue *q;
     49    vq_action vqa;
     50    while (Size >= sizeof(vqa)) {
     51        memcpy(&vqa, Data, sizeof(vqa));
     52        Data += sizeof(vqa);
     53        Size -= sizeof(vqa);
     54
     55        q = net_if->queues[vqa.queue % 3];
     56
     57        vqa.length = vqa.length >= Size ? Size :  vqa.length;
     58
     59        /*
     60         * Only attempt to write incoming packets, when using the socket
     61         * backend. Otherwise, always place the input on a virtqueue.
     62         */
     63        if (vqa.rx && sockfds_initialized) {
     64            int ignored = write(sockfds[0], Data, vqa.length);
     65            (void) ignored;
     66        } else {
     67            vqa.rx = 0;
     68            uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
     69            /*
     70             * If checking used ring, ensure that the fuzzer doesn't trigger
     71             * trivial asserion failure on zero-zied buffer
     72             */
     73            qtest_memwrite(s, req_addr, Data, vqa.length);
     74
     75
     76            free_head = qvirtqueue_add(s, q, req_addr, vqa.length,
     77                    vqa.write, vqa.next);
     78            qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
     79            qvirtqueue_kick(s, dev, q, free_head);
     80        }
     81
     82        /* Run the main loop */
     83        qtest_clock_step(s, 100);
     84        flush_events(s);
     85
     86        /* Wait on used descriptors */
     87        if (check_used && !vqa.rx) {
     88            gint64 start_time = g_get_monotonic_time();
     89            /*
     90             * normally, we could just use qvirtio_wait_used_elem, but since we
     91             * must manually run the main-loop for all the bhs to run, we use
     92             * this hack with flush_events(), to run the main_loop
     93             */
     94            while (!vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) {
     95                uint32_t got_desc_idx;
     96                /* Input led to a virtio_error */
     97                if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
     98                    break;
     99                }
    100                if (dev->bus->get_queue_isr_status(dev, q) &&
    101                        qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) {
    102                    g_assert_cmpint(got_desc_idx, ==, free_head);
    103                    break;
    104                }
    105                g_assert(g_get_monotonic_time() - start_time
    106                        <= QVIRTIO_NET_TIMEOUT_US);
    107
    108                /* Run the main loop */
    109                qtest_clock_step(s, 100);
    110                flush_events(s);
    111            }
    112        }
    113        Data += vqa.length;
    114        Size -= vqa.length;
    115    }
    116}
    117
    118static void virtio_net_fork_fuzz(QTestState *s,
    119        const unsigned char *Data, size_t Size)
    120{
    121    if (fork() == 0) {
    122        virtio_net_fuzz_multi(s, Data, Size, false);
    123        flush_events(s);
    124        _Exit(0);
    125    } else {
    126        flush_events(s);
    127        wait(NULL);
    128    }
    129}
    130
    131static void virtio_net_fork_fuzz_check_used(QTestState *s,
    132        const unsigned char *Data, size_t Size)
    133{
    134    if (fork() == 0) {
    135        virtio_net_fuzz_multi(s, Data, Size, true);
    136        flush_events(s);
    137        _Exit(0);
    138    } else {
    139        flush_events(s);
    140        wait(NULL);
    141    }
    142}
    143
    144static void virtio_net_pre_fuzz(QTestState *s)
    145{
    146    qos_init_path(s);
    147    counter_shm_init();
    148}
    149
    150static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg)
    151{
    152    int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds);
    153    g_assert_cmpint(ret, !=, -1);
    154    fcntl(sockfds[0], F_SETFL, O_NONBLOCK);
    155    sockfds_initialized = true;
    156    g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ",
    157                           sockfds[1]);
    158    return arg;
    159}
    160
    161static void *virtio_net_test_setup_user(GString *cmd_line, void *arg)
    162{
    163    g_string_append_printf(cmd_line, " -netdev user,id=hs0 ");
    164    return arg;
    165}
    166
    167static void register_virtio_net_fuzz_targets(void)
    168{
    169    fuzz_add_qos_target(&(FuzzTarget){
    170            .name = "virtio-net-socket",
    171            .description = "Fuzz the virtio-net virtual queues. Fuzz incoming "
    172            "traffic using the socket backend",
    173            .pre_fuzz = &virtio_net_pre_fuzz,
    174            .fuzz = virtio_net_fork_fuzz,},
    175            "virtio-net",
    176            &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
    177            );
    178
    179    fuzz_add_qos_target(&(FuzzTarget){
    180            .name = "virtio-net-socket-check-used",
    181            .description = "Fuzz the virtio-net virtual queues. Wait for the "
    182            "descriptors to be used. Timeout may indicate improperly handled "
    183            "input",
    184            .pre_fuzz = &virtio_net_pre_fuzz,
    185            .fuzz = virtio_net_fork_fuzz_check_used,},
    186            "virtio-net",
    187            &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
    188            );
    189    fuzz_add_qos_target(&(FuzzTarget){
    190            .name = "virtio-net-slirp",
    191            .description = "Fuzz the virtio-net virtual queues with the slirp "
    192            " backend. Warning: May result in network traffic emitted from the "
    193            " process. Run in an isolated network environment.",
    194            .pre_fuzz = &virtio_net_pre_fuzz,
    195            .fuzz = virtio_net_fork_fuzz,},
    196            "virtio-net",
    197            &(QOSGraphTestOptions){.before = virtio_net_test_setup_user}
    198            );
    199}
    200
    201fuzz_target_init(register_virtio_net_fuzz_targets);