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
22
23 #define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
24 #define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
25
26 /*
27 * the input to the fuzzing functions below is a buffer of random bytes. we
28 * want to convert these bytes into a sequence of qtest or qos calls. to do
29 * this we define some opcodes:
30 */
31 enum action_id {
32 WRITEB,
33 WRITEW,
34 WRITEL,
35 READB,
36 READW,
37 READL,
38 ACTION_MAX
39 };
40
ioport_fuzz_qtest(QTestState * s,const unsigned char * Data,size_t Size)41 static void ioport_fuzz_qtest(QTestState *s,
42 const unsigned char *Data, size_t Size) {
43 /*
44 * loop over the Data, breaking it up into actions. each action has an
45 * opcode, address offset and value
46 */
47 struct {
48 uint8_t opcode;
49 uint8_t addr;
50 uint32_t value;
51 } a;
52
53 while (Size >= sizeof(a)) {
54 /* make a copy of the action so we can normalize the values in-place */
55 memcpy(&a, Data, sizeof(a));
56 /* select between two i440fx Port IO addresses */
57 uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
58 I440FX_PCI_HOST_BRIDGE_DATA;
59 switch (a.opcode % ACTION_MAX) {
60 case WRITEB:
61 qtest_outb(s, addr, (uint8_t)a.value);
62 break;
63 case WRITEW:
64 qtest_outw(s, addr, (uint16_t)a.value);
65 break;
66 case WRITEL:
67 qtest_outl(s, addr, (uint32_t)a.value);
68 break;
69 case READB:
70 qtest_inb(s, addr);
71 break;
72 case READW:
73 qtest_inw(s, addr);
74 break;
75 case READL:
76 qtest_inl(s, addr);
77 break;
78 }
79 /* Move to the next operation */
80 Size -= sizeof(a);
81 Data += sizeof(a);
82 }
83 flush_events(s);
84 }
85
i440fx_fuzz_qtest(QTestState * s,const unsigned char * Data,size_t Size)86 static void i440fx_fuzz_qtest(QTestState *s,
87 const unsigned char *Data,
88 size_t Size)
89 {
90 ioport_fuzz_qtest(s, Data, Size);
91 fuzz_reset(s);
92 }
93
pciconfig_fuzz_qos(QTestState * s,QPCIBus * bus,const unsigned char * Data,size_t Size)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
i440fx_fuzz_qos(QTestState * s,const unsigned char * Data,size_t Size)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 const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
149 " -m 0 -display none";
i440fx_argv(FuzzTarget * t)150 static GString *i440fx_argv(FuzzTarget *t)
151 {
152 return g_string_new(i440fx_qtest_argv);
153 }
154
155
register_pci_fuzz_targets(void)156 static void register_pci_fuzz_targets(void)
157 {
158 /* Uses simple qtest commands and reboots to reset state */
159 fuzz_add_target(&(FuzzTarget){
160 .name = "i440fx-qtest-reboot-fuzz",
161 .description = "Fuzz the i440fx using raw qtest commands and "
162 "rebooting after each run",
163 .get_init_cmdline = i440fx_argv,
164 .fuzz = i440fx_fuzz_qtest});
165
166
167 /*
168 * Uses libqos. Doesn't do anything to reset state. Note that if we were to
169 * reboot after each run, we would also have to redo the qos-related
170 * initialization (qos_init_path)
171 */
172 fuzz_add_qos_target(&(FuzzTarget){
173 .name = "i440fx-qos-noreset-fuzz",
174 .description = "Fuzz the i440fx using raw qtest commands and "
175 "rebooting after each run",
176 .fuzz = i440fx_fuzz_qos,},
177 "i440FX-pcihost",
178 &(QOSGraphTestOptions){}
179 );
180 }
181
182 fuzz_target_init(register_pci_fuzz_targets);
183