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 */ 31 enum action_id { 32 WRITEB, 33 WRITEW, 34 WRITEL, 35 READB, 36 READW, 37 READL, 38 ACTION_MAX 39 }; 40 41 static 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 86 static 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 94 static 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 135 static 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 148 static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest" 149 " -m 0 -display none"; 150 static GString *i440fx_argv(FuzzTarget *t) 151 { 152 return g_string_new(i440fx_qtest_argv); 153 } 154 155 156 static 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 182 fuzz_target_init(register_pci_fuzz_targets); 183