/* * I440FX Fuzzing Target * * Copyright Red Hat Inc., 2019 * * Authors: * Alexander Bulekov * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "tests/qtest/libqtest.h" #include "tests/qtest/libqos/pci.h" #include "tests/qtest/libqos/pci-pc.h" #include "fuzz.h" #include "fuzz/qos_fuzz.h" #include "fuzz/fork_fuzz.h" #define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8 #define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc /* * the input to the fuzzing functions below is a buffer of random bytes. we * want to convert these bytes into a sequence of qtest or qos calls. to do * this we define some opcodes: */ enum action_id { WRITEB, WRITEW, WRITEL, READB, READW, READL, ACTION_MAX }; static void i440fx_fuzz_qtest(QTestState *s, const unsigned char *Data, size_t Size) { /* * loop over the Data, breaking it up into actions. each action has an * opcode, address offset and value */ typedef struct QTestFuzzAction { uint8_t opcode; uint8_t addr; uint32_t value; } QTestFuzzAction; QTestFuzzAction a; while (Size >= sizeof(a)) { /* make a copy of the action so we can normalize the values in-place */ memcpy(&a, Data, sizeof(a)); /* select between two i440fx Port IO addresses */ uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG : I440FX_PCI_HOST_BRIDGE_DATA; switch (a.opcode % ACTION_MAX) { case WRITEB: qtest_outb(s, addr, (uint8_t)a.value); break; case WRITEW: qtest_outw(s, addr, (uint16_t)a.value); break; case WRITEL: qtest_outl(s, addr, (uint32_t)a.value); break; case READB: qtest_inb(s, addr); break; case READW: qtest_inw(s, addr); break; case READL: qtest_inl(s, addr); break; } /* Move to the next operation */ Size -= sizeof(a); Data += sizeof(a); } flush_events(s); } static void i440fx_fuzz_qos(QTestState *s, const unsigned char *Data, size_t Size) { /* * Same as i440fx_fuzz_qtest, but using QOS. devfn is incorporated into the * value written over Port IO */ typedef struct QOSFuzzAction { uint8_t opcode; uint8_t offset; int devfn; uint32_t value; } QOSFuzzAction; static QPCIBus *bus; if (!bus) { bus = qpci_new_pc(s, fuzz_qos_alloc); } QOSFuzzAction a; while (Size >= sizeof(a)) { memcpy(&a, Data, sizeof(a)); switch (a.opcode % ACTION_MAX) { case WRITEB: bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value); break; case WRITEW: bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value); break; case WRITEL: bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value); break; case READB: bus->config_readb(bus, a.devfn, a.offset); break; case READW: bus->config_readw(bus, a.devfn, a.offset); break; case READL: bus->config_readl(bus, a.devfn, a.offset); break; } Size -= sizeof(a); Data += sizeof(a); } flush_events(s); } static void i440fx_fuzz_qos_fork(QTestState *s, const unsigned char *Data, size_t Size) { if (fork() == 0) { i440fx_fuzz_qos(s, Data, Size); _Exit(0); } else { wait(NULL); } } static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest" "-m 0 -display none"; static const char *i440fx_argv(FuzzTarget *t) { return i440fx_qtest_argv; } static void fork_init(void) { counter_shm_init(); } static void register_pci_fuzz_targets(void) { /* Uses simple qtest commands and reboots to reset state */ fuzz_add_target(&(FuzzTarget){ .name = "i440fx-qtest-reboot-fuzz", .description = "Fuzz the i440fx using raw qtest commands and" "rebooting after each run", .get_init_cmdline = i440fx_argv, .fuzz = i440fx_fuzz_qtest}); /* Uses libqos and forks to prevent state leakage */ fuzz_add_qos_target(&(FuzzTarget){ .name = "i440fx-qos-fork-fuzz", .description = "Fuzz the i440fx using raw qtest commands and" "rebooting after each run", .pre_vm_init = &fork_init, .fuzz = i440fx_fuzz_qos_fork,}, "i440FX-pcihost", &(QOSGraphTestOptions){} ); /* * Uses libqos. Doesn't do anything to reset state. Note that if we were to * reboot after each run, we would also have to redo the qos-related * initialization (qos_init_path) */ fuzz_add_qos_target(&(FuzzTarget){ .name = "i440fx-qos-noreset-fuzz", .description = "Fuzz the i440fx using raw qtest commands and" "rebooting after each run", .fuzz = i440fx_fuzz_qos,}, "i440FX-pcihost", &(QOSGraphTestOptions){} ); } fuzz_target_init(register_pci_fuzz_targets);