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/libqos/libqtest.h"
  17#include "tests/qtest/libqos/pci.h"
  18#include "tests/qtest/libqos/pci-pc.h"
  19#include "fuzz.h"
  20#include "qos_fuzz.h"
  21#include "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 ioport_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    struct {
  49        uint8_t opcode;
  50        uint8_t addr;
  51        uint32_t value;
  52    } a;
  53
  54    while (Size >= sizeof(a)) {
  55        /* make a copy of the action so we can normalize the values in-place */
  56        memcpy(&a, Data, sizeof(a));
  57        /* select between two i440fx Port IO addresses */
  58        uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
  59                                      I440FX_PCI_HOST_BRIDGE_DATA;
  60        switch (a.opcode % ACTION_MAX) {
  61        case WRITEB:
  62            qtest_outb(s, addr, (uint8_t)a.value);
  63            break;
  64        case WRITEW:
  65            qtest_outw(s, addr, (uint16_t)a.value);
  66            break;
  67        case WRITEL:
  68            qtest_outl(s, addr, (uint32_t)a.value);
  69            break;
  70        case READB:
  71            qtest_inb(s, addr);
  72            break;
  73        case READW:
  74            qtest_inw(s, addr);
  75            break;
  76        case READL:
  77            qtest_inl(s, addr);
  78            break;
  79        }
  80        /* Move to the next operation */
  81        Size -= sizeof(a);
  82        Data += sizeof(a);
  83    }
  84    flush_events(s);
  85}
  86
  87static void i440fx_fuzz_qtest(QTestState *s,
  88                              const unsigned char *Data,
  89                              size_t Size)
  90{
  91    ioport_fuzz_qtest(s, Data, Size);
  92}
  93
  94static void pciconfig_fuzz_qos(QTestState *s, QPCIBus *bus,
  95        const unsigned char *Data, size_t Size) {
  96    /*
  97     * Same as ioport_fuzz_qtest, but using QOS. devfn is incorporated into the
  98     * value written over Port IO
  99     */
 100    struct {
 101        uint8_t opcode;
 102        uint8_t offset;
 103        int devfn;
 104        uint32_t value;
 105    } a;
 106
 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(QTestState *s,
 136                            const unsigned char *Data,
 137                            size_t Size)
 138{
 139    static QPCIBus *bus;
 140
 141    if (!bus) {
 142        bus = qpci_new_pc(s, fuzz_qos_alloc);
 143    }
 144
 145    pciconfig_fuzz_qos(s, bus, Data, Size);
 146}
 147
 148static void i440fx_fuzz_qos_fork(QTestState *s,
 149        const unsigned char *Data, size_t Size) {
 150    if (fork() == 0) {
 151        i440fx_fuzz_qos(s, Data, Size);
 152        _Exit(0);
 153    } else {
 154        flush_events(s);
 155        wait(NULL);
 156    }
 157}
 158
 159static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
 160                                       " -m 0 -display none";
 161static GString *i440fx_argv(FuzzTarget *t)
 162{
 163    return g_string_new(i440fx_qtest_argv);
 164}
 165
 166static void fork_init(void)
 167{
 168    counter_shm_init();
 169}
 170
 171static void register_pci_fuzz_targets(void)
 172{
 173    /* Uses simple qtest commands and reboots to reset state */
 174    fuzz_add_target(&(FuzzTarget){
 175                .name = "i440fx-qtest-reboot-fuzz",
 176                .description = "Fuzz the i440fx using raw qtest commands and "
 177                               "rebooting after each run",
 178                .get_init_cmdline = i440fx_argv,
 179                .fuzz = i440fx_fuzz_qtest});
 180
 181    /* Uses libqos and forks to prevent state leakage */
 182    fuzz_add_qos_target(&(FuzzTarget){
 183                .name = "i440fx-qos-fork-fuzz",
 184                .description = "Fuzz the i440fx using raw qtest commands and "
 185                               "rebooting after each run",
 186                .pre_vm_init = &fork_init,
 187                .fuzz = i440fx_fuzz_qos_fork,},
 188                "i440FX-pcihost",
 189                &(QOSGraphTestOptions){}
 190                );
 191
 192    /*
 193     * Uses libqos. Doesn't do anything to reset state. Note that if we were to
 194     * reboot after each run, we would also have to redo the qos-related
 195     * initialization (qos_init_path)
 196     */
 197    fuzz_add_qos_target(&(FuzzTarget){
 198                .name = "i440fx-qos-noreset-fuzz",
 199                .description = "Fuzz the i440fx using raw qtest commands and "
 200                               "rebooting after each run",
 201                .fuzz = i440fx_fuzz_qos,},
 202                "i440FX-pcihost",
 203                &(QOSGraphTestOptions){}
 204                );
 205}
 206
 207fuzz_target_init(register_pci_fuzz_targets);
 208