@@ -6,6 +6,9 @@ fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
+# Targets
+fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
+
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
# Linker Script to force coverage-counters into known regions which we can mark
new file mode 100644
@@ -0,0 +1,178 @@
+/*
+ * I440FX Fuzzing Target
+ *
+ * Copyright Red Hat Inc., 2019
+ *
+ * Authors:
+ * Alexander Bulekov <alxndr@bu.edu>
+ *
+ * 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
+
+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) {
+ typedef struct QTestFuzzAction {
+ uint32_t value;
+ uint8_t id;
+ uint8_t addr;
+ } QTestFuzzAction;
+ QTestFuzzAction a;
+
+ while (Size >= sizeof(a)) {
+ memcpy(&a, Data, sizeof(a));
+ uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
+ I440FX_PCI_HOST_BRIDGE_DATA;
+ switch (a.id % 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;
+ }
+ Size -= sizeof(a);
+ Data += sizeof(a);
+ }
+ flush_events(s);
+}
+
+static void i440fx_fuzz_qos(QTestState *s,
+ const unsigned char *Data, size_t Size) {
+
+ typedef struct QOSFuzzAction {
+ uint32_t value;
+ int devfn;
+ uint8_t offset;
+ uint8_t id;
+ } 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.id % 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);