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 i440fx_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 typedef struct QTestFuzzAction { 49 uint8_t opcode; 50 uint8_t addr; 51 uint32_t value; 52 } QTestFuzzAction; 53 QTestFuzzAction a; 54 55 while (Size >= sizeof(a)) { 56 /* make a copy of the action so we can normalize the values in-place */ 57 memcpy(&a, Data, sizeof(a)); 58 /* select between two i440fx Port IO addresses */ 59 uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG : 60 I440FX_PCI_HOST_BRIDGE_DATA; 61 switch (a.opcode % ACTION_MAX) { 62 case WRITEB: 63 qtest_outb(s, addr, (uint8_t)a.value); 64 break; 65 case WRITEW: 66 qtest_outw(s, addr, (uint16_t)a.value); 67 break; 68 case WRITEL: 69 qtest_outl(s, addr, (uint32_t)a.value); 70 break; 71 case READB: 72 qtest_inb(s, addr); 73 break; 74 case READW: 75 qtest_inw(s, addr); 76 break; 77 case READL: 78 qtest_inl(s, addr); 79 break; 80 } 81 /* Move to the next operation */ 82 Size -= sizeof(a); 83 Data += sizeof(a); 84 } 85 flush_events(s); 86 } 87 88 static void i440fx_fuzz_qos(QTestState *s, 89 const unsigned char *Data, size_t Size) { 90 /* 91 * Same as i440fx_fuzz_qtest, but using QOS. devfn is incorporated into the 92 * value written over Port IO 93 */ 94 typedef struct QOSFuzzAction { 95 uint8_t opcode; 96 uint8_t offset; 97 int devfn; 98 uint32_t value; 99 } QOSFuzzAction; 100 101 static QPCIBus *bus; 102 if (!bus) { 103 bus = qpci_new_pc(s, fuzz_qos_alloc); 104 } 105 106 QOSFuzzAction a; 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_fork(QTestState *s, 136 const unsigned char *Data, size_t Size) { 137 if (fork() == 0) { 138 i440fx_fuzz_qos(s, Data, Size); 139 _Exit(0); 140 } else { 141 wait(NULL); 142 } 143 } 144 145 static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest" 146 "-m 0 -display none"; 147 static const char *i440fx_argv(FuzzTarget *t) 148 { 149 return i440fx_qtest_argv; 150 } 151 152 static void fork_init(void) 153 { 154 counter_shm_init(); 155 } 156 157 static void register_pci_fuzz_targets(void) 158 { 159 /* Uses simple qtest commands and reboots to reset state */ 160 fuzz_add_target(&(FuzzTarget){ 161 .name = "i440fx-qtest-reboot-fuzz", 162 .description = "Fuzz the i440fx using raw qtest commands and" 163 "rebooting after each run", 164 .get_init_cmdline = i440fx_argv, 165 .fuzz = i440fx_fuzz_qtest}); 166 167 /* Uses libqos and forks to prevent state leakage */ 168 fuzz_add_qos_target(&(FuzzTarget){ 169 .name = "i440fx-qos-fork-fuzz", 170 .description = "Fuzz the i440fx using raw qtest commands and" 171 "rebooting after each run", 172 .pre_vm_init = &fork_init, 173 .fuzz = i440fx_fuzz_qos_fork,}, 174 "i440FX-pcihost", 175 &(QOSGraphTestOptions){} 176 ); 177 178 /* 179 * Uses libqos. Doesn't do anything to reset state. Note that if we were to 180 * reboot after each run, we would also have to redo the qos-related 181 * initialization (qos_init_path) 182 */ 183 fuzz_add_qos_target(&(FuzzTarget){ 184 .name = "i440fx-qos-noreset-fuzz", 185 .description = "Fuzz the i440fx using raw qtest commands and" 186 "rebooting after each run", 187 .fuzz = i440fx_fuzz_qos,}, 188 "i440FX-pcihost", 189 &(QOSGraphTestOptions){} 190 ); 191 } 192 193 fuzz_target_init(register_pci_fuzz_targets); 194