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 "qos_fuzz.h"
  21
  22
  23#define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
  24#define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
  25
  26/*
  27 * the input to the fuzzing functions below is a buffer of random bytes. we
  28 * want to convert these bytes into a sequence of qtest or qos calls. to do
  29 * this we define some opcodes:
  30 */
  31enum action_id {
  32    WRITEB,
  33    WRITEW,
  34    WRITEL,
  35    READB,
  36    READW,
  37    READL,
  38    ACTION_MAX
  39};
  40
  41static void ioport_fuzz_qtest(QTestState *s,
  42        const unsigned char *Data, size_t Size) {
  43    /*
  44     * loop over the Data, breaking it up into actions. each action has an
  45     * opcode, address offset and value
  46     */
  47    struct {
  48        uint8_t opcode;
  49        uint8_t addr;
  50        uint32_t value;
  51    } a;
  52
  53    while (Size >= sizeof(a)) {
  54        /* make a copy of the action so we can normalize the values in-place */
  55        memcpy(&a, Data, sizeof(a));
  56        /* select between two i440fx Port IO addresses */
  57        uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
  58                                      I440FX_PCI_HOST_BRIDGE_DATA;
  59        switch (a.opcode % ACTION_MAX) {
  60        case WRITEB:
  61            qtest_outb(s, addr, (uint8_t)a.value);
  62            break;
  63        case WRITEW:
  64            qtest_outw(s, addr, (uint16_t)a.value);
  65            break;
  66        case WRITEL:
  67            qtest_outl(s, addr, (uint32_t)a.value);
  68            break;
  69        case READB:
  70            qtest_inb(s, addr);
  71            break;
  72        case READW:
  73            qtest_inw(s, addr);
  74            break;
  75        case READL:
  76            qtest_inl(s, addr);
  77            break;
  78        }
  79        /* Move to the next operation */
  80        Size -= sizeof(a);
  81        Data += sizeof(a);
  82    }
  83    flush_events(s);
  84}
  85
  86static void i440fx_fuzz_qtest(QTestState *s,
  87                              const unsigned char *Data,
  88                              size_t Size)
  89{
  90    ioport_fuzz_qtest(s, Data, Size);
  91    fuzz_reset(s);
  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 const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
 149                                       " -m 0 -display none";
 150static GString *i440fx_argv(FuzzTarget *t)
 151{
 152    return g_string_new(i440fx_qtest_argv);
 153}
 154
 155
 156static void register_pci_fuzz_targets(void)
 157{
 158    /* Uses simple qtest commands and reboots to reset state */
 159    fuzz_add_target(&(FuzzTarget){
 160                .name = "i440fx-qtest-reboot-fuzz",
 161                .description = "Fuzz the i440fx using raw qtest commands and "
 162                               "rebooting after each run",
 163                .get_init_cmdline = i440fx_argv,
 164                .fuzz = i440fx_fuzz_qtest});
 165
 166
 167    /*
 168     * Uses libqos. Doesn't do anything to reset state. Note that if we were to
 169     * reboot after each run, we would also have to redo the qos-related
 170     * initialization (qos_init_path)
 171     */
 172    fuzz_add_qos_target(&(FuzzTarget){
 173                .name = "i440fx-qos-noreset-fuzz",
 174                .description = "Fuzz the i440fx using raw qtest commands and "
 175                               "rebooting after each run",
 176                .fuzz = i440fx_fuzz_qos,},
 177                "i440FX-pcihost",
 178                &(QOSGraphTestOptions){}
 179                );
 180}
 181
 182fuzz_target_init(register_pci_fuzz_targets);
 183