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