qemu/tests/qtest/fuzz/i440fx_fuzz.c
<<
>>
Prefs
   1/*
   2 * I440FX 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 "qemu/main-loop.h"
  16#include "tests/qtest/libqtest.h"
  17#include "tests/qtest/libqos/pci.h"
  18#include "tests/qtest/libqos/pci-pc.h"
  19#include "fuzz.h"
  20#include "fuzz/qos_fuzz.h"
  21#include "fuzz/fork_fuzz.h"
  22
  23
  24#define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
  25#define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
  26
  27/*
  28 * the input to the fuzzing functions below is a buffer of random bytes. we
  29 * want to convert these bytes into a sequence of qtest or qos calls. to do
  30 * this we define some opcodes:
  31 */
  32enum action_id {
  33    WRITEB,
  34    WRITEW,
  35    WRITEL,
  36    READB,
  37    READW,
  38    READL,
  39    ACTION_MAX
  40};
  41
  42static void i440fx_fuzz_qtest(QTestState *s,
  43        const unsigned char *Data, size_t Size) {
  44    /*
  45     * loop over the Data, breaking it up into actions. each action has an
  46     * opcode, address offset and value
  47     */
  48    typedef struct QTestFuzzAction {
  49        uint8_t opcode;
  50        uint8_t addr;
  51        uint32_t value;
  52    } QTestFuzzAction;
  53    QTestFuzzAction a;
  54
  55    while (Size >= sizeof(a)) {
  56        /* make a copy of the action so we can normalize the values in-place */
  57        memcpy(&a, Data, sizeof(a));
  58        /* select between two i440fx Port IO addresses */
  59        uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
  60                                      I440FX_PCI_HOST_BRIDGE_DATA;
  61        switch (a.opcode % ACTION_MAX) {
  62        case WRITEB:
  63            qtest_outb(s, addr, (uint8_t)a.value);
  64            break;
  65        case WRITEW:
  66            qtest_outw(s, addr, (uint16_t)a.value);
  67            break;
  68        case WRITEL:
  69            qtest_outl(s, addr, (uint32_t)a.value);
  70            break;
  71        case READB:
  72            qtest_inb(s, addr);
  73            break;
  74        case READW:
  75            qtest_inw(s, addr);
  76            break;
  77        case READL:
  78            qtest_inl(s, addr);
  79            break;
  80        }
  81        /* Move to the next operation */
  82        Size -= sizeof(a);
  83        Data += sizeof(a);
  84    }
  85    flush_events(s);
  86}
  87
  88static void i440fx_fuzz_qos(QTestState *s,
  89        const unsigned char *Data, size_t Size) {
  90    /*
  91     * Same as i440fx_fuzz_qtest, but using QOS. devfn is incorporated into the
  92     * value written over Port IO
  93     */
  94    typedef struct QOSFuzzAction {
  95        uint8_t opcode;
  96        uint8_t offset;
  97        int devfn;
  98        uint32_t value;
  99    } QOSFuzzAction;
 100
 101    static QPCIBus *bus;
 102    if (!bus) {
 103        bus = qpci_new_pc(s, fuzz_qos_alloc);
 104    }
 105
 106    QOSFuzzAction a;
 107    while (Size >= sizeof(a)) {
 108        memcpy(&a, Data, sizeof(a));
 109        switch (a.opcode % ACTION_MAX) {
 110        case WRITEB:
 111            bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value);
 112            break;
 113        case WRITEW:
 114            bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value);
 115            break;
 116        case WRITEL:
 117            bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value);
 118            break;
 119        case READB:
 120            bus->config_readb(bus, a.devfn, a.offset);
 121            break;
 122        case READW:
 123            bus->config_readw(bus, a.devfn, a.offset);
 124            break;
 125        case READL:
 126            bus->config_readl(bus, a.devfn, a.offset);
 127            break;
 128        }
 129        Size -= sizeof(a);
 130        Data += sizeof(a);
 131    }
 132    flush_events(s);
 133}
 134
 135static void i440fx_fuzz_qos_fork(QTestState *s,
 136        const unsigned char *Data, size_t Size) {
 137    if (fork() == 0) {
 138        i440fx_fuzz_qos(s, Data, Size);
 139        _Exit(0);
 140    } else {
 141        wait(NULL);
 142    }
 143}
 144
 145static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
 146                                       "-m 0 -display none";
 147static const char *i440fx_argv(FuzzTarget *t)
 148{
 149    return i440fx_qtest_argv;
 150}
 151
 152static void fork_init(void)
 153{
 154    counter_shm_init();
 155}
 156
 157static void register_pci_fuzz_targets(void)
 158{
 159    /* Uses simple qtest commands and reboots to reset state */
 160    fuzz_add_target(&(FuzzTarget){
 161                .name = "i440fx-qtest-reboot-fuzz",
 162                .description = "Fuzz the i440fx using raw qtest commands and"
 163                               "rebooting after each run",
 164                .get_init_cmdline = i440fx_argv,
 165                .fuzz = i440fx_fuzz_qtest});
 166
 167    /* Uses libqos and forks to prevent state leakage */
 168    fuzz_add_qos_target(&(FuzzTarget){
 169                .name = "i440fx-qos-fork-fuzz",
 170                .description = "Fuzz the i440fx using raw qtest commands and"
 171                               "rebooting after each run",
 172                .pre_vm_init = &fork_init,
 173                .fuzz = i440fx_fuzz_qos_fork,},
 174                "i440FX-pcihost",
 175                &(QOSGraphTestOptions){}
 176                );
 177
 178    /*
 179     * Uses libqos. Doesn't do anything to reset state. Note that if we were to
 180     * reboot after each run, we would also have to redo the qos-related
 181     * initialization (qos_init_path)
 182     */
 183    fuzz_add_qos_target(&(FuzzTarget){
 184                .name = "i440fx-qos-noreset-fuzz",
 185                .description = "Fuzz the i440fx using raw qtest commands and"
 186                               "rebooting after each run",
 187                .fuzz = i440fx_fuzz_qos,},
 188                "i440FX-pcihost",
 189                &(QOSGraphTestOptions){}
 190                );
 191}
 192
 193fuzz_target_init(register_pci_fuzz_targets);
 194