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 "fuzz/qos_fuzz.h" 21 #include "fuzz/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 */ 32 enum action_id { 33 WRITEB, 34 WRITEW, 35 WRITEL, 36 READB, 37 READW, 38 READL, 39 ACTION_MAX 40 }; 41 42 static 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 87 static 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 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 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 159 static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest" 160 " -m 0 -display none"; 161 static GString *i440fx_argv(FuzzTarget *t) 162 { 163 return g_string_new(i440fx_qtest_argv); 164 } 165 166 static void fork_init(void) 167 { 168 counter_shm_init(); 169 } 170 171 static 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 207 fuzz_target_init(register_pci_fuzz_targets); 208