xref: /openbmc/qemu/tests/qtest/fuzz/i440fx_fuzz.c (revision 88cd34ee)
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