リビジョン | 275ab39d86974aab8bbce14b1a0c488653cc72d2 (tree) |
---|---|
日時 | 2020-02-22 17:26:48 |
作者 | Alexander Bulekov <alxndr@bu.e...> |
コミッター | Stefan Hajnoczi |
fuzz: add support for qos-assisted fuzz targets
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Message-id: 20200220041118.23264-17-alxndr@bu.edu
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
@@ -1,8 +1,10 @@ | ||
1 | 1 | QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF) |
2 | 2 | |
3 | 3 | fuzz-obj-y += tests/qtest/libqtest.o |
4 | +fuzz-obj-y += $(libqos-obj-y) | |
4 | 5 | fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton |
5 | 6 | fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o |
7 | +fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o | |
6 | 8 | |
7 | 9 | FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest |
8 | 10 |
@@ -0,0 +1,234 @@ | ||
1 | +/* | |
2 | + * QOS-assisted fuzzing helpers | |
3 | + * | |
4 | + * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com> | |
5 | + * | |
6 | + * This library is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU Lesser General Public | |
8 | + * License version 2 as published by the Free Software Foundation. | |
9 | + * | |
10 | + * This library is distributed in the hope that it will be useful, | |
11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | + * Lesser General Public License for more details. | |
14 | + * | |
15 | + * You should have received a copy of the GNU Lesser General Public | |
16 | + * License along with this library; if not, see <http://www.gnu.org/licenses/> | |
17 | + */ | |
18 | + | |
19 | +#include "qemu/osdep.h" | |
20 | +#include "qemu/units.h" | |
21 | +#include "qapi/error.h" | |
22 | +#include "qemu-common.h" | |
23 | +#include "exec/memory.h" | |
24 | +#include "exec/address-spaces.h" | |
25 | +#include "sysemu/sysemu.h" | |
26 | +#include "qemu/main-loop.h" | |
27 | + | |
28 | +#include "tests/qtest/libqtest.h" | |
29 | +#include "tests/qtest/libqos/malloc.h" | |
30 | +#include "tests/qtest/libqos/qgraph.h" | |
31 | +#include "tests/qtest/libqos/qgraph_internal.h" | |
32 | +#include "tests/qtest/libqos/qos_external.h" | |
33 | + | |
34 | +#include "fuzz.h" | |
35 | +#include "qos_fuzz.h" | |
36 | + | |
37 | +#include "qapi/qapi-commands-machine.h" | |
38 | +#include "qapi/qapi-commands-qom.h" | |
39 | +#include "qapi/qmp/qlist.h" | |
40 | + | |
41 | + | |
42 | +void *fuzz_qos_obj; | |
43 | +QGuestAllocator *fuzz_qos_alloc; | |
44 | + | |
45 | +static const char *fuzz_target_name; | |
46 | +static char **fuzz_path_vec; | |
47 | + | |
48 | +/* | |
49 | + * Replaced the qmp commands with direct qmp_marshal calls. | |
50 | + * Probably there is a better way to do this | |
51 | + */ | |
52 | +static void qos_set_machines_devices_available(void) | |
53 | +{ | |
54 | + QDict *req = qdict_new(); | |
55 | + QObject *response; | |
56 | + QDict *args = qdict_new(); | |
57 | + QList *lst; | |
58 | + Error *err = NULL; | |
59 | + | |
60 | + qmp_marshal_query_machines(NULL, &response, &err); | |
61 | + assert(!err); | |
62 | + lst = qobject_to(QList, response); | |
63 | + apply_to_qlist(lst, true); | |
64 | + | |
65 | + qobject_unref(response); | |
66 | + | |
67 | + | |
68 | + qdict_put_str(req, "execute", "qom-list-types"); | |
69 | + qdict_put_str(args, "implements", "device"); | |
70 | + qdict_put_bool(args, "abstract", true); | |
71 | + qdict_put_obj(req, "arguments", (QObject *) args); | |
72 | + | |
73 | + qmp_marshal_qom_list_types(args, &response, &err); | |
74 | + assert(!err); | |
75 | + lst = qobject_to(QList, response); | |
76 | + apply_to_qlist(lst, false); | |
77 | + qobject_unref(response); | |
78 | + qobject_unref(req); | |
79 | +} | |
80 | + | |
81 | +static char **current_path; | |
82 | + | |
83 | +void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc) | |
84 | +{ | |
85 | + return allocate_objects(qts, current_path + 1, p_alloc); | |
86 | +} | |
87 | + | |
88 | +static const char *qos_build_main_args(void) | |
89 | +{ | |
90 | + char **path = fuzz_path_vec; | |
91 | + QOSGraphNode *test_node; | |
92 | + GString *cmd_line = g_string_new(path[0]); | |
93 | + void *test_arg; | |
94 | + | |
95 | + if (!path) { | |
96 | + fprintf(stderr, "QOS Path not found\n"); | |
97 | + abort(); | |
98 | + } | |
99 | + | |
100 | + /* Before test */ | |
101 | + current_path = path; | |
102 | + test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]); | |
103 | + test_arg = test_node->u.test.arg; | |
104 | + if (test_node->u.test.before) { | |
105 | + test_arg = test_node->u.test.before(cmd_line, test_arg); | |
106 | + } | |
107 | + /* Prepend the arguments that we need */ | |
108 | + g_string_prepend(cmd_line, | |
109 | + TARGET_NAME " -display none -machine accel=qtest -m 64 "); | |
110 | + return cmd_line->str; | |
111 | +} | |
112 | + | |
113 | +/* | |
114 | + * This function is largely a copy of qos-test.c:walk_path. Since walk_path | |
115 | + * is itself a callback, its a little annoying to add another argument/layer of | |
116 | + * indirection | |
117 | + */ | |
118 | +static void walk_path(QOSGraphNode *orig_path, int len) | |
119 | +{ | |
120 | + QOSGraphNode *path; | |
121 | + QOSGraphEdge *edge; | |
122 | + | |
123 | + /* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */ | |
124 | + QOSEdgeType etype = QEDGE_CONSUMED_BY; | |
125 | + | |
126 | + /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */ | |
127 | + char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2)); | |
128 | + int path_vec_size = 0; | |
129 | + | |
130 | + char *after_cmd, *before_cmd, *after_device; | |
131 | + GString *after_device_str = g_string_new(""); | |
132 | + char *node_name = orig_path->name, *path_str; | |
133 | + | |
134 | + GString *cmd_line = g_string_new(""); | |
135 | + GString *cmd_line2 = g_string_new(""); | |
136 | + | |
137 | + path = qos_graph_get_node(node_name); /* root */ | |
138 | + node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */ | |
139 | + | |
140 | + path_vec[path_vec_size++] = node_name; | |
141 | + path_vec[path_vec_size++] = qos_get_machine_type(node_name); | |
142 | + | |
143 | + for (;;) { | |
144 | + path = qos_graph_get_node(node_name); | |
145 | + if (!path->path_edge) { | |
146 | + break; | |
147 | + } | |
148 | + | |
149 | + node_name = qos_graph_edge_get_dest(path->path_edge); | |
150 | + | |
151 | + /* append node command line + previous edge command line */ | |
152 | + if (path->command_line && etype == QEDGE_CONSUMED_BY) { | |
153 | + g_string_append(cmd_line, path->command_line); | |
154 | + g_string_append(cmd_line, after_device_str->str); | |
155 | + g_string_truncate(after_device_str, 0); | |
156 | + } | |
157 | + | |
158 | + path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge); | |
159 | + /* detect if edge has command line args */ | |
160 | + after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge); | |
161 | + after_device = qos_graph_edge_get_extra_device_opts(path->path_edge); | |
162 | + before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge); | |
163 | + edge = qos_graph_get_edge(path->name, node_name); | |
164 | + etype = qos_graph_edge_get_type(edge); | |
165 | + | |
166 | + if (before_cmd) { | |
167 | + g_string_append(cmd_line, before_cmd); | |
168 | + } | |
169 | + if (after_cmd) { | |
170 | + g_string_append(cmd_line2, after_cmd); | |
171 | + } | |
172 | + if (after_device) { | |
173 | + g_string_append(after_device_str, after_device); | |
174 | + } | |
175 | + } | |
176 | + | |
177 | + path_vec[path_vec_size++] = NULL; | |
178 | + g_string_append(cmd_line, after_device_str->str); | |
179 | + g_string_free(after_device_str, true); | |
180 | + | |
181 | + g_string_append(cmd_line, cmd_line2->str); | |
182 | + g_string_free(cmd_line2, true); | |
183 | + | |
184 | + /* | |
185 | + * here position 0 has <arch>/<machine>, position 1 has <machine>. | |
186 | + * The path must not have the <arch>, qtest_add_data_func adds it. | |
187 | + */ | |
188 | + path_str = g_strjoinv("/", path_vec + 1); | |
189 | + | |
190 | + /* Check that this is the test we care about: */ | |
191 | + char *test_name = strrchr(path_str, '/') + 1; | |
192 | + if (strcmp(test_name, fuzz_target_name) == 0) { | |
193 | + /* | |
194 | + * put arch/machine in position 1 so run_one_test can do its work | |
195 | + * and add the command line at position 0. | |
196 | + */ | |
197 | + path_vec[1] = path_vec[0]; | |
198 | + path_vec[0] = g_string_free(cmd_line, false); | |
199 | + | |
200 | + fuzz_path_vec = path_vec; | |
201 | + } else { | |
202 | + g_free(path_vec); | |
203 | + } | |
204 | + | |
205 | + g_free(path_str); | |
206 | +} | |
207 | + | |
208 | +static const char *qos_get_cmdline(FuzzTarget *t) | |
209 | +{ | |
210 | + /* | |
211 | + * Set a global variable that we use to identify the qos_path for our | |
212 | + * fuzz_target | |
213 | + */ | |
214 | + fuzz_target_name = t->name; | |
215 | + qos_set_machines_devices_available(); | |
216 | + qos_graph_foreach_test_path(walk_path); | |
217 | + return qos_build_main_args(); | |
218 | +} | |
219 | + | |
220 | +void fuzz_add_qos_target( | |
221 | + FuzzTarget *fuzz_opts, | |
222 | + const char *interface, | |
223 | + QOSGraphTestOptions *opts | |
224 | + ) | |
225 | +{ | |
226 | + qos_add_test(fuzz_opts->name, interface, NULL, opts); | |
227 | + fuzz_opts->get_init_cmdline = qos_get_cmdline; | |
228 | + fuzz_add_target(fuzz_opts); | |
229 | +} | |
230 | + | |
231 | +void qos_init_path(QTestState *s) | |
232 | +{ | |
233 | + fuzz_qos_obj = qos_allocate_objects(s , &fuzz_qos_alloc); | |
234 | +} |
@@ -0,0 +1,33 @@ | ||
1 | +/* | |
2 | + * QOS-assisted fuzzing helpers | |
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 | +#ifndef _QOS_FUZZ_H_ | |
14 | +#define _QOS_FUZZ_H_ | |
15 | + | |
16 | +#include "tests/qtest/fuzz/fuzz.h" | |
17 | +#include "tests/qtest/libqos/qgraph.h" | |
18 | + | |
19 | +int qos_fuzz(const unsigned char *Data, size_t Size); | |
20 | +void qos_setup(void); | |
21 | + | |
22 | +extern void *fuzz_qos_obj; | |
23 | +extern QGuestAllocator *fuzz_qos_alloc; | |
24 | + | |
25 | +void fuzz_add_qos_target( | |
26 | + FuzzTarget *fuzz_opts, | |
27 | + const char *interface, | |
28 | + QOSGraphTestOptions *opts | |
29 | + ); | |
30 | + | |
31 | +void qos_init_path(QTestState *); | |
32 | + | |
33 | +#endif |