blob: b17fc725dfd6be08b79c08ef22b309ef9a5d302f [file] [log] [blame]
Alexander Bulekov04f71322020-02-19 23:11:15 -05001/*
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"
Marc-André Lureau907b5102022-03-30 13:39:05 +040016#include "tests/qtest/libqtest.h"
Alexander Bulekov04f71322020-02-19 23:11:15 -050017#include "tests/qtest/libqos/pci.h"
18#include "tests/qtest/libqos/pci-pc.h"
19#include "fuzz.h"
Paolo Bonzini64ed6f92020-08-03 17:04:25 +020020#include "qos_fuzz.h"
21#include "fork_fuzz.h"
Alexander Bulekov04f71322020-02-19 23:11:15 -050022
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 */
32enum action_id {
33 WRITEB,
34 WRITEW,
35 WRITEL,
36 READB,
37 READW,
38 READL,
39 ACTION_MAX
40};
41
Philippe Mathieu-Daudé6fb5f082020-05-14 16:34:33 +020042static void ioport_fuzz_qtest(QTestState *s,
Alexander Bulekov04f71322020-02-19 23:11:15 -050043 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 */
Philippe Mathieu-Daudé79e18a62020-05-14 16:34:31 +020048 struct {
Alexander Bulekov04f71322020-02-19 23:11:15 -050049 uint8_t opcode;
50 uint8_t addr;
51 uint32_t value;
Philippe Mathieu-Daudé79e18a62020-05-14 16:34:31 +020052 } a;
Alexander Bulekov04f71322020-02-19 23:11:15 -050053
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
Philippe Mathieu-Daudé6fb5f082020-05-14 16:34:33 +020087static 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
Philippe Mathieu-Daudé84cb0a62020-05-14 16:34:32 +020094static void pciconfig_fuzz_qos(QTestState *s, QPCIBus *bus,
Alexander Bulekov04f71322020-02-19 23:11:15 -050095 const unsigned char *Data, size_t Size) {
96 /*
Philippe Mathieu-Daudé6fb5f082020-05-14 16:34:33 +020097 * Same as ioport_fuzz_qtest, but using QOS. devfn is incorporated into the
Alexander Bulekov04f71322020-02-19 23:11:15 -050098 * value written over Port IO
99 */
Philippe Mathieu-Daudé79e18a62020-05-14 16:34:31 +0200100 struct {
Alexander Bulekov04f71322020-02-19 23:11:15 -0500101 uint8_t opcode;
102 uint8_t offset;
103 int devfn;
104 uint32_t value;
Philippe Mathieu-Daudé79e18a62020-05-14 16:34:31 +0200105 } a;
Alexander Bulekov04f71322020-02-19 23:11:15 -0500106
Alexander Bulekov04f71322020-02-19 23:11:15 -0500107 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
Philippe Mathieu-Daudé84cb0a62020-05-14 16:34:32 +0200135static 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
Alexander Bulekov04f71322020-02-19 23:11:15 -0500148static 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 {
Alexander Bulekovdfd5ddb2020-05-11 23:01:33 -0400154 flush_events(s);
Alexander Bulekov04f71322020-02-19 23:11:15 -0500155 wait(NULL);
156 }
157}
158
159static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
Alexander Bulekov68518032020-05-11 23:01:31 -0400160 " -m 0 -display none";
Alexander Bulekovf5ec79f2020-07-14 13:46:16 -0400161static GString *i440fx_argv(FuzzTarget *t)
Alexander Bulekov04f71322020-02-19 23:11:15 -0500162{
Alexander Bulekovf5ec79f2020-07-14 13:46:16 -0400163 return g_string_new(i440fx_qtest_argv);
Alexander Bulekov04f71322020-02-19 23:11:15 -0500164}
165
166static void fork_init(void)
167{
168 counter_shm_init();
169}
170
171static 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",
Philippe Mathieu-Daudé73ee6da2020-05-14 16:34:30 +0200176 .description = "Fuzz the i440fx using raw qtest commands and "
Alexander Bulekov04f71322020-02-19 23:11:15 -0500177 "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",
Philippe Mathieu-Daudé73ee6da2020-05-14 16:34:30 +0200184 .description = "Fuzz the i440fx using raw qtest commands and "
Alexander Bulekov04f71322020-02-19 23:11:15 -0500185 "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",
Philippe Mathieu-Daudé73ee6da2020-05-14 16:34:30 +0200199 .description = "Fuzz the i440fx using raw qtest commands and "
Alexander Bulekov04f71322020-02-19 23:11:15 -0500200 "rebooting after each run",
201 .fuzz = i440fx_fuzz_qos,},
202 "i440FX-pcihost",
203 &(QOSGraphTestOptions){}
204 );
205}
206
207fuzz_target_init(register_pci_fuzz_targets);