qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

i440fx_fuzz.c (5887B)


      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 #include "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);