104f71324SAlexander Bulekov /* 204f71324SAlexander Bulekov * I440FX Fuzzing Target 304f71324SAlexander Bulekov * 404f71324SAlexander Bulekov * Copyright Red Hat Inc., 2019 504f71324SAlexander Bulekov * 604f71324SAlexander Bulekov * Authors: 704f71324SAlexander Bulekov * Alexander Bulekov <alxndr@bu.edu> 804f71324SAlexander Bulekov * 904f71324SAlexander Bulekov * This work is licensed under the terms of the GNU GPL, version 2 or later. 1004f71324SAlexander Bulekov * See the COPYING file in the top-level directory. 1104f71324SAlexander Bulekov */ 1204f71324SAlexander Bulekov 1304f71324SAlexander Bulekov #include "qemu/osdep.h" 1404f71324SAlexander Bulekov 1504f71324SAlexander Bulekov #include "qemu/main-loop.h" 1604f71324SAlexander Bulekov #include "tests/qtest/libqtest.h" 1704f71324SAlexander Bulekov #include "tests/qtest/libqos/pci.h" 1804f71324SAlexander Bulekov #include "tests/qtest/libqos/pci-pc.h" 1904f71324SAlexander Bulekov #include "fuzz.h" 2004f71324SAlexander Bulekov #include "fuzz/qos_fuzz.h" 2104f71324SAlexander Bulekov #include "fuzz/fork_fuzz.h" 2204f71324SAlexander Bulekov 2304f71324SAlexander Bulekov 2404f71324SAlexander Bulekov #define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8 2504f71324SAlexander Bulekov #define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc 2604f71324SAlexander Bulekov 2704f71324SAlexander Bulekov /* 2804f71324SAlexander Bulekov * the input to the fuzzing functions below is a buffer of random bytes. we 2904f71324SAlexander Bulekov * want to convert these bytes into a sequence of qtest or qos calls. to do 3004f71324SAlexander Bulekov * this we define some opcodes: 3104f71324SAlexander Bulekov */ 3204f71324SAlexander Bulekov enum action_id { 3304f71324SAlexander Bulekov WRITEB, 3404f71324SAlexander Bulekov WRITEW, 3504f71324SAlexander Bulekov WRITEL, 3604f71324SAlexander Bulekov READB, 3704f71324SAlexander Bulekov READW, 3804f71324SAlexander Bulekov READL, 3904f71324SAlexander Bulekov ACTION_MAX 4004f71324SAlexander Bulekov }; 4104f71324SAlexander Bulekov 42*6fb5f084SPhilippe Mathieu-Daudé static void ioport_fuzz_qtest(QTestState *s, 4304f71324SAlexander Bulekov const unsigned char *Data, size_t Size) { 4404f71324SAlexander Bulekov /* 4504f71324SAlexander Bulekov * loop over the Data, breaking it up into actions. each action has an 4604f71324SAlexander Bulekov * opcode, address offset and value 4704f71324SAlexander Bulekov */ 4879e18a60SPhilippe Mathieu-Daudé struct { 4904f71324SAlexander Bulekov uint8_t opcode; 5004f71324SAlexander Bulekov uint8_t addr; 5104f71324SAlexander Bulekov uint32_t value; 5279e18a60SPhilippe Mathieu-Daudé } a; 5304f71324SAlexander Bulekov 5404f71324SAlexander Bulekov while (Size >= sizeof(a)) { 5504f71324SAlexander Bulekov /* make a copy of the action so we can normalize the values in-place */ 5604f71324SAlexander Bulekov memcpy(&a, Data, sizeof(a)); 5704f71324SAlexander Bulekov /* select between two i440fx Port IO addresses */ 5804f71324SAlexander Bulekov uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG : 5904f71324SAlexander Bulekov I440FX_PCI_HOST_BRIDGE_DATA; 6004f71324SAlexander Bulekov switch (a.opcode % ACTION_MAX) { 6104f71324SAlexander Bulekov case WRITEB: 6204f71324SAlexander Bulekov qtest_outb(s, addr, (uint8_t)a.value); 6304f71324SAlexander Bulekov break; 6404f71324SAlexander Bulekov case WRITEW: 6504f71324SAlexander Bulekov qtest_outw(s, addr, (uint16_t)a.value); 6604f71324SAlexander Bulekov break; 6704f71324SAlexander Bulekov case WRITEL: 6804f71324SAlexander Bulekov qtest_outl(s, addr, (uint32_t)a.value); 6904f71324SAlexander Bulekov break; 7004f71324SAlexander Bulekov case READB: 7104f71324SAlexander Bulekov qtest_inb(s, addr); 7204f71324SAlexander Bulekov break; 7304f71324SAlexander Bulekov case READW: 7404f71324SAlexander Bulekov qtest_inw(s, addr); 7504f71324SAlexander Bulekov break; 7604f71324SAlexander Bulekov case READL: 7704f71324SAlexander Bulekov qtest_inl(s, addr); 7804f71324SAlexander Bulekov break; 7904f71324SAlexander Bulekov } 8004f71324SAlexander Bulekov /* Move to the next operation */ 8104f71324SAlexander Bulekov Size -= sizeof(a); 8204f71324SAlexander Bulekov Data += sizeof(a); 8304f71324SAlexander Bulekov } 8404f71324SAlexander Bulekov flush_events(s); 8504f71324SAlexander Bulekov } 8604f71324SAlexander Bulekov 87*6fb5f084SPhilippe Mathieu-Daudé static void i440fx_fuzz_qtest(QTestState *s, 88*6fb5f084SPhilippe Mathieu-Daudé const unsigned char *Data, 89*6fb5f084SPhilippe Mathieu-Daudé size_t Size) 90*6fb5f084SPhilippe Mathieu-Daudé { 91*6fb5f084SPhilippe Mathieu-Daudé ioport_fuzz_qtest(s, Data, Size); 92*6fb5f084SPhilippe Mathieu-Daudé } 93*6fb5f084SPhilippe Mathieu-Daudé 9484cb0a6dSPhilippe Mathieu-Daudé static void pciconfig_fuzz_qos(QTestState *s, QPCIBus *bus, 9504f71324SAlexander Bulekov const unsigned char *Data, size_t Size) { 9604f71324SAlexander Bulekov /* 97*6fb5f084SPhilippe Mathieu-Daudé * Same as ioport_fuzz_qtest, but using QOS. devfn is incorporated into the 9804f71324SAlexander Bulekov * value written over Port IO 9904f71324SAlexander Bulekov */ 10079e18a60SPhilippe Mathieu-Daudé struct { 10104f71324SAlexander Bulekov uint8_t opcode; 10204f71324SAlexander Bulekov uint8_t offset; 10304f71324SAlexander Bulekov int devfn; 10404f71324SAlexander Bulekov uint32_t value; 10579e18a60SPhilippe Mathieu-Daudé } a; 10604f71324SAlexander Bulekov 10704f71324SAlexander Bulekov while (Size >= sizeof(a)) { 10804f71324SAlexander Bulekov memcpy(&a, Data, sizeof(a)); 10904f71324SAlexander Bulekov switch (a.opcode % ACTION_MAX) { 11004f71324SAlexander Bulekov case WRITEB: 11104f71324SAlexander Bulekov bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value); 11204f71324SAlexander Bulekov break; 11304f71324SAlexander Bulekov case WRITEW: 11404f71324SAlexander Bulekov bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value); 11504f71324SAlexander Bulekov break; 11604f71324SAlexander Bulekov case WRITEL: 11704f71324SAlexander Bulekov bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value); 11804f71324SAlexander Bulekov break; 11904f71324SAlexander Bulekov case READB: 12004f71324SAlexander Bulekov bus->config_readb(bus, a.devfn, a.offset); 12104f71324SAlexander Bulekov break; 12204f71324SAlexander Bulekov case READW: 12304f71324SAlexander Bulekov bus->config_readw(bus, a.devfn, a.offset); 12404f71324SAlexander Bulekov break; 12504f71324SAlexander Bulekov case READL: 12604f71324SAlexander Bulekov bus->config_readl(bus, a.devfn, a.offset); 12704f71324SAlexander Bulekov break; 12804f71324SAlexander Bulekov } 12904f71324SAlexander Bulekov Size -= sizeof(a); 13004f71324SAlexander Bulekov Data += sizeof(a); 13104f71324SAlexander Bulekov } 13204f71324SAlexander Bulekov flush_events(s); 13304f71324SAlexander Bulekov } 13404f71324SAlexander Bulekov 13584cb0a6dSPhilippe Mathieu-Daudé static void i440fx_fuzz_qos(QTestState *s, 13684cb0a6dSPhilippe Mathieu-Daudé const unsigned char *Data, 13784cb0a6dSPhilippe Mathieu-Daudé size_t Size) 13884cb0a6dSPhilippe Mathieu-Daudé { 13984cb0a6dSPhilippe Mathieu-Daudé static QPCIBus *bus; 14084cb0a6dSPhilippe Mathieu-Daudé 14184cb0a6dSPhilippe Mathieu-Daudé if (!bus) { 14284cb0a6dSPhilippe Mathieu-Daudé bus = qpci_new_pc(s, fuzz_qos_alloc); 14384cb0a6dSPhilippe Mathieu-Daudé } 14484cb0a6dSPhilippe Mathieu-Daudé 14584cb0a6dSPhilippe Mathieu-Daudé pciconfig_fuzz_qos(s, bus, Data, Size); 14684cb0a6dSPhilippe Mathieu-Daudé } 14784cb0a6dSPhilippe Mathieu-Daudé 14804f71324SAlexander Bulekov static void i440fx_fuzz_qos_fork(QTestState *s, 14904f71324SAlexander Bulekov const unsigned char *Data, size_t Size) { 15004f71324SAlexander Bulekov if (fork() == 0) { 15104f71324SAlexander Bulekov i440fx_fuzz_qos(s, Data, Size); 15204f71324SAlexander Bulekov _Exit(0); 15304f71324SAlexander Bulekov } else { 15404f71324SAlexander Bulekov wait(NULL); 15504f71324SAlexander Bulekov } 15604f71324SAlexander Bulekov } 15704f71324SAlexander Bulekov 15804f71324SAlexander Bulekov static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest" 15904f71324SAlexander Bulekov "-m 0 -display none"; 16004f71324SAlexander Bulekov static const char *i440fx_argv(FuzzTarget *t) 16104f71324SAlexander Bulekov { 16204f71324SAlexander Bulekov return i440fx_qtest_argv; 16304f71324SAlexander Bulekov } 16404f71324SAlexander Bulekov 16504f71324SAlexander Bulekov static void fork_init(void) 16604f71324SAlexander Bulekov { 16704f71324SAlexander Bulekov counter_shm_init(); 16804f71324SAlexander Bulekov } 16904f71324SAlexander Bulekov 17004f71324SAlexander Bulekov static void register_pci_fuzz_targets(void) 17104f71324SAlexander Bulekov { 17204f71324SAlexander Bulekov /* Uses simple qtest commands and reboots to reset state */ 17304f71324SAlexander Bulekov fuzz_add_target(&(FuzzTarget){ 17404f71324SAlexander Bulekov .name = "i440fx-qtest-reboot-fuzz", 17504f71324SAlexander Bulekov .description = "Fuzz the i440fx using raw qtest commands and " 17604f71324SAlexander Bulekov "rebooting after each run", 17704f71324SAlexander Bulekov .get_init_cmdline = i440fx_argv, 17804f71324SAlexander Bulekov .fuzz = i440fx_fuzz_qtest}); 17904f71324SAlexander Bulekov 18004f71324SAlexander Bulekov /* Uses libqos and forks to prevent state leakage */ 18104f71324SAlexander Bulekov fuzz_add_qos_target(&(FuzzTarget){ 18204f71324SAlexander Bulekov .name = "i440fx-qos-fork-fuzz", 18304f71324SAlexander Bulekov .description = "Fuzz the i440fx using raw qtest commands and " 18404f71324SAlexander Bulekov "rebooting after each run", 18504f71324SAlexander Bulekov .pre_vm_init = &fork_init, 18604f71324SAlexander Bulekov .fuzz = i440fx_fuzz_qos_fork,}, 18704f71324SAlexander Bulekov "i440FX-pcihost", 18804f71324SAlexander Bulekov &(QOSGraphTestOptions){} 18904f71324SAlexander Bulekov ); 19004f71324SAlexander Bulekov 19104f71324SAlexander Bulekov /* 19204f71324SAlexander Bulekov * Uses libqos. Doesn't do anything to reset state. Note that if we were to 19304f71324SAlexander Bulekov * reboot after each run, we would also have to redo the qos-related 19404f71324SAlexander Bulekov * initialization (qos_init_path) 19504f71324SAlexander Bulekov */ 19604f71324SAlexander Bulekov fuzz_add_qos_target(&(FuzzTarget){ 19704f71324SAlexander Bulekov .name = "i440fx-qos-noreset-fuzz", 19804f71324SAlexander Bulekov .description = "Fuzz the i440fx using raw qtest commands and " 19904f71324SAlexander Bulekov "rebooting after each run", 20004f71324SAlexander Bulekov .fuzz = i440fx_fuzz_qos,}, 20104f71324SAlexander Bulekov "i440FX-pcihost", 20204f71324SAlexander Bulekov &(QOSGraphTestOptions){} 20304f71324SAlexander Bulekov ); 20404f71324SAlexander Bulekov } 20504f71324SAlexander Bulekov 20604f71324SAlexander Bulekov fuzz_target_init(register_pci_fuzz_targets); 207