]>
Commit | Line | Data |
---|---|---|
5f6fd09a AB |
1 | /* |
2 | * fuzzing driver | |
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 | ||
14 | #include "qemu/osdep.h" | |
15 | ||
16 | #include <wordexp.h> | |
17 | ||
18 | #include "sysemu/qtest.h" | |
19 | #include "sysemu/runstate.h" | |
20 | #include "sysemu/sysemu.h" | |
21 | #include "qemu/main-loop.h" | |
22 | #include "tests/qtest/libqtest.h" | |
23 | #include "tests/qtest/libqos/qgraph.h" | |
24 | #include "fuzz.h" | |
25 | ||
26 | #define MAX_EVENT_LOOPS 10 | |
27 | ||
28 | typedef struct FuzzTargetState { | |
29 | FuzzTarget *target; | |
30 | QSLIST_ENTRY(FuzzTargetState) target_list; | |
31 | } FuzzTargetState; | |
32 | ||
33 | typedef QSLIST_HEAD(, FuzzTargetState) FuzzTargetList; | |
34 | ||
35 | static const char *fuzz_arch = TARGET_NAME; | |
36 | ||
37 | static FuzzTargetList *fuzz_target_list; | |
38 | static FuzzTarget *fuzz_target; | |
39 | static QTestState *fuzz_qts; | |
40 | ||
41 | ||
42 | ||
43 | void flush_events(QTestState *s) | |
44 | { | |
45 | int i = MAX_EVENT_LOOPS; | |
46 | while (g_main_context_pending(NULL) && i-- > 0) { | |
47 | main_loop_wait(false); | |
48 | } | |
49 | } | |
50 | ||
51 | static QTestState *qtest_setup(void) | |
52 | { | |
53 | qtest_server_set_send_handler(&qtest_client_inproc_recv, &fuzz_qts); | |
54 | return qtest_inproc_init(&fuzz_qts, false, fuzz_arch, | |
55 | &qtest_server_inproc_recv); | |
56 | } | |
57 | ||
58 | void fuzz_add_target(const FuzzTarget *target) | |
59 | { | |
60 | FuzzTargetState *tmp; | |
61 | FuzzTargetState *target_state; | |
62 | if (!fuzz_target_list) { | |
63 | fuzz_target_list = g_new0(FuzzTargetList, 1); | |
64 | } | |
65 | ||
66 | QSLIST_FOREACH(tmp, fuzz_target_list, target_list) { | |
67 | if (g_strcmp0(tmp->target->name, target->name) == 0) { | |
68 | fprintf(stderr, "Error: Fuzz target name %s already in use\n", | |
69 | target->name); | |
70 | abort(); | |
71 | } | |
72 | } | |
73 | target_state = g_new0(FuzzTargetState, 1); | |
74 | target_state->target = g_new0(FuzzTarget, 1); | |
75 | *(target_state->target) = *target; | |
76 | QSLIST_INSERT_HEAD(fuzz_target_list, target_state, target_list); | |
77 | } | |
78 | ||
79 | ||
80 | ||
81 | static void usage(char *path) | |
82 | { | |
83 | printf("Usage: %s --fuzz-target=FUZZ_TARGET [LIBFUZZER ARGUMENTS]\n", path); | |
84 | printf("where FUZZ_TARGET is one of:\n"); | |
85 | FuzzTargetState *tmp; | |
86 | if (!fuzz_target_list) { | |
87 | fprintf(stderr, "Fuzz target list not initialized\n"); | |
88 | abort(); | |
89 | } | |
90 | QSLIST_FOREACH(tmp, fuzz_target_list, target_list) { | |
91 | printf(" * %s : %s\n", tmp->target->name, | |
92 | tmp->target->description); | |
93 | } | |
94 | exit(0); | |
95 | } | |
96 | ||
97 | static FuzzTarget *fuzz_get_target(char* name) | |
98 | { | |
99 | FuzzTargetState *tmp; | |
100 | if (!fuzz_target_list) { | |
101 | fprintf(stderr, "Fuzz target list not initialized\n"); | |
102 | abort(); | |
103 | } | |
104 | ||
105 | QSLIST_FOREACH(tmp, fuzz_target_list, target_list) { | |
106 | if (strcmp(tmp->target->name, name) == 0) { | |
107 | return tmp->target; | |
108 | } | |
109 | } | |
110 | return NULL; | |
111 | } | |
112 | ||
113 | ||
114 | /* Executed for each fuzzing-input */ | |
115 | int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size) | |
116 | { | |
117 | /* | |
118 | * Do the pre-fuzz-initialization before the first fuzzing iteration, | |
119 | * instead of before the actual fuzz loop. This is needed since libfuzzer | |
120 | * may fork off additional workers, prior to the fuzzing loop, and if | |
121 | * pre_fuzz() sets up e.g. shared memory, this should be done for the | |
122 | * individual worker processes | |
123 | */ | |
124 | static int pre_fuzz_done; | |
125 | if (!pre_fuzz_done && fuzz_target->pre_fuzz) { | |
126 | fuzz_target->pre_fuzz(fuzz_qts); | |
127 | pre_fuzz_done = true; | |
128 | } | |
129 | ||
130 | fuzz_target->fuzz(fuzz_qts, Data, Size); | |
131 | return 0; | |
132 | } | |
133 | ||
134 | /* Executed once, prior to fuzzing */ | |
135 | int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) | |
136 | { | |
137 | ||
138 | char *target_name; | |
139 | ||
140 | /* Initialize qgraph and modules */ | |
141 | qos_graph_init(); | |
142 | module_call_init(MODULE_INIT_FUZZ_TARGET); | |
143 | module_call_init(MODULE_INIT_QOM); | |
144 | module_call_init(MODULE_INIT_LIBQOS); | |
145 | ||
146 | if (*argc <= 1) { | |
147 | usage(**argv); | |
148 | } | |
149 | ||
150 | /* Identify the fuzz target */ | |
151 | target_name = (*argv)[1]; | |
152 | if (!strstr(target_name, "--fuzz-target=")) { | |
153 | usage(**argv); | |
154 | } | |
155 | ||
156 | target_name += strlen("--fuzz-target="); | |
157 | ||
158 | fuzz_target = fuzz_get_target(target_name); | |
159 | if (!fuzz_target) { | |
160 | usage(**argv); | |
161 | } | |
162 | ||
163 | fuzz_qts = qtest_setup(); | |
164 | ||
165 | if (fuzz_target->pre_vm_init) { | |
166 | fuzz_target->pre_vm_init(); | |
167 | } | |
168 | ||
169 | /* Run QEMU's softmmu main with the fuzz-target dependent arguments */ | |
170 | const char *init_cmdline = fuzz_target->get_init_cmdline(fuzz_target); | |
171 | ||
172 | /* Split the runcmd into an argv and argc */ | |
173 | wordexp_t result; | |
174 | wordexp(init_cmdline, &result, 0); | |
175 | ||
176 | qemu_init(result.we_wordc, result.we_wordv, NULL); | |
177 | ||
178 | return 0; | |
179 | } |