2 # -*- coding: utf-8 -*-
5 Convert plain qtest traces to C or Bash reproducers
7 Use this to help build bug-reports or create in-tree reproducers for bugs.
8 Note: This will not format C code for you. Pipe the output through
9 clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, ColumnLimit: 90}"
17 from datetime
import date
19 __author__
= "Alexander Bulekov <alxndr@bu.edu>"
20 __copyright__
= "Copyright (C) 2021, Red Hat, Inc."
21 __license__
= "GPL version 2 or (at your option) any later version"
23 __maintainer__
= "Alexander Bulekov"
24 __email__
= "alxndr@bu.edu"
29 * Autogenerated Fuzzer Test Case
31 * Copyright (c) {date} {owner}
33 * This work is licensed under the terms of the GNU GPL, version 2 or later.
34 * See the COPYING file in the top-level directory.
37 #include "qemu/osdep.h"
41 """.format(date
=date
.today().year
, owner
=owner
)
44 """ Return a multi-line C comment. Assume the text is already wrapped """
45 return "/*\n * " + "\n * ".join(s
.splitlines()) + "\n*/"
47 def print_c_function(s
):
49 for l
in s
.splitlines():
50 print(" * {}".format(l
))
52 def bash_reproducer(path
, args
, trace
):
53 result
= '\\\n'.join(textwrap
.wrap("cat << EOF | {} {}".format(path
, args
),
54 72, break_on_hyphens
=False,
55 drop_whitespace
=False))
56 for l
in trace
.splitlines():
57 result
+= "\n" + '\\\n'.join(textwrap
.wrap(l
,72,drop_whitespace
=False))
61 def c_reproducer(name
, args
, trace
):
63 result
.append("""static void {}(void)\n{{""".format(name
))
65 # libqtest will add its own qtest args, so get rid of them
66 args
= args
.replace("-accel qtest","")
67 args
= args
.replace(",accel=qtest","")
68 args
= args
.replace("-machine accel=qtest","")
69 args
= args
.replace("-qtest stdio","")
70 result
.append("""QTestState *s = qtest_init("{}");""".format(args
))
71 for l
in trace
.splitlines():
75 buf
= param
[3][2:] #Get the 0x... buffer and trim the "0x"
76 assert len(buf
)%2 == 0
77 bufbytes
= [buf
[i
:i
+2] for i
in range(0, len(buf
), 2)]
78 bufstring
= '\\x'+'\\x'.join(bufbytes
)
81 result
.append("""qtest_bufwrite(s, {}, "{}", {});""".format(
82 addr
, bufstring
, size
))
83 elif cmd
.startswith("in") or cmd
.startswith("read"):
84 result
.append("qtest_{}(s, {});".format(
86 elif cmd
.startswith("out") or cmd
.startswith("write"):
87 result
.append("qtest_{}(s, {}, {});".format(
88 cmd
, param
[1], param
[2]))
89 elif cmd
== "clock_step":
91 result
.append("qtest_clock_step_next(s);")
93 result
.append("qtest_clock_step(s, {});".format(param
[1]))
94 result
.append("qtest_quit(s);\n}")
95 return "\n".join(result
)
97 def c_main(name
, arch
):
98 return """int main(int argc, char **argv)
100 const char *arch = qtest_get_arch();
102 g_test_init(&argc, &argv, NULL);
104 if (strcmp(arch, "{arch}") == 0) {{
105 qtest_add_func("fuzz/{name}",{name});
109 }}""".format(name
=name
, arch
=arch
)
112 parser
= argparse
.ArgumentParser()
113 group
= parser
.add_mutually_exclusive_group()
114 group
.add_argument("-bash", help="Only output a copy-pastable bash command",
116 group
.add_argument("-c", help="Only output a c function",
118 parser
.add_argument('-owner', help="If generating complete C source code, \
119 this specifies the Copyright owner",
120 nargs
='?', default
="<name of author>")
121 parser
.add_argument("-no_comment", help="Don't include a bash reproducer \
122 as a comment in the C reproducers",
124 parser
.add_argument('-name', help="The name of the c function",
125 nargs
='?', default
="test_fuzz")
126 parser
.add_argument('input_trace', help="input QTest command sequence \
128 nargs
='?', type=argparse
.FileType('r'),
130 args
= parser
.parse_args()
132 qemu_path
= os
.getenv("QEMU_PATH")
133 qemu_args
= os
.getenv("QEMU_ARGS")
134 if not qemu_args
or not qemu_path
:
135 print("Please set QEMU_PATH and QEMU_ARGS environment variables")
138 bash_args
= qemu_args
139 if " -qtest stdio" not in qemu_args
:
140 bash_args
+= " -qtest stdio"
142 arch
= qemu_path
.split("-")[-1]
143 trace
= args
.input_trace
.read().strip()
146 print(bash_reproducer(qemu_path
, bash_args
, trace
))
150 output
+= c_header(args
.owner
) + "\n"
151 if not args
.no_comment
:
152 output
+= c_comment(bash_reproducer(qemu_path
, bash_args
, trace
))
153 output
+= c_reproducer(args
.name
, qemu_args
, trace
)
155 output
+= c_main(args
.name
, arch
)
159 if __name__
== '__main__':