From: Paolo Bonzini <pbonzini@redhat.com>
To: qemu-devel@nongnu.org
Cc: Alexander Bulekov <alxndr@bu.edu>,
Darren Kenny <darren.kenny@oracle.com>
Subject: [PULL 06/16] fuzz: add a script to build reproducers
Date: Tue, 16 Mar 2021 17:15:21 -0400 [thread overview]
Message-ID: <20210316211531.1649909-7-pbonzini@redhat.com> (raw)
In-Reply-To: <20210316211531.1649909-1-pbonzini@redhat.com>
From: Alexander Bulekov <alxndr@bu.edu>
Currently, bash and C crash reproducers are be built manually. This is a
problem, as we want to integrate reproducers into the tree, for
regression testing. This patch adds a script that converts a sequence of
QTest commands into a pasteable Bash reproducer, or a libqtest-based C
program. This will try to wrap pasteable reproducers to 72 chars, but
the generated C code will not have nice formatting. Therefore, the C
output of this script should be piped through an auto-formatter, such as
clang-format
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
scripts/oss-fuzz/output_reproducer.py | 160 ++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
create mode 100755 scripts/oss-fuzz/output_reproducer.py
diff --git a/scripts/oss-fuzz/output_reproducer.py b/scripts/oss-fuzz/output_reproducer.py
new file mode 100755
index 0000000000..3608b0600e
--- /dev/null
+++ b/scripts/oss-fuzz/output_reproducer.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+Convert plain qtest traces to C or Bash reproducers
+
+Use this to help build bug-reports or create in-tree reproducers for bugs.
+Note: This will not format C code for you. Pipe the output through
+clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, ColumnLimit: 90}"
+or similar
+"""
+
+import sys
+import os
+import argparse
+import textwrap
+from datetime import date
+
+__author__ = "Alexander Bulekov <alxndr@bu.edu>"
+__copyright__ = "Copyright (C) 2021, Red Hat, Inc."
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Alexander Bulekov"
+__email__ = "alxndr@bu.edu"
+
+
+def c_header(owner):
+ return """/*
+ * Autogenerated Fuzzer Test Case
+ *
+ * Copyright (c) {date} {owner}
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "libqos/libqtest.h"
+
+ """.format(date=date.today().year, owner=owner)
+
+def c_comment(s):
+ """ Return a multi-line C comment. Assume the text is already wrapped """
+ return "/*\n * " + "\n * ".join(s.splitlines()) + "\n*/"
+
+def print_c_function(s):
+ print("/* ")
+ for l in s.splitlines():
+ print(" * {}".format(l))
+
+def bash_reproducer(path, args, trace):
+ result = '\\\n'.join(textwrap.wrap("cat << EOF | {} {}".format(path, args),
+ 72, break_on_hyphens=False,
+ drop_whitespace=False))
+ for l in trace.splitlines():
+ result += "\n" + '\\\n'.join(textwrap.wrap(l,72,drop_whitespace=False))
+ result += "\nEOF"
+ return result
+
+def c_reproducer(name, args, trace):
+ result = []
+ result.append("""static void {}(void)\n{{""".format(name))
+
+ # libqtest will add its own qtest args, so get rid of them
+ args = args.replace("-accel qtest","")
+ args = args.replace(",accel=qtest","")
+ args = args.replace("-machine accel=qtest","")
+ args = args.replace("-qtest stdio","")
+ result.append("""QTestState *s = qtest_init("{}");""".format(args))
+ for l in trace.splitlines():
+ param = l.split()
+ cmd = param[0]
+ if cmd == "write":
+ buf = param[3][2:] #Get the 0x... buffer and trim the "0x"
+ assert len(buf)%2 == 0
+ bufbytes = [buf[i:i+2] for i in range(0, len(buf), 2)]
+ bufstring = '\\x'+'\\x'.join(bufbytes)
+ addr = param[1]
+ size = param[2]
+ result.append("""qtest_bufwrite(s, {}, "{}", {});""".format(
+ addr, bufstring, size))
+ elif cmd.startswith("in") or cmd.startswith("read"):
+ result.append("qtest_{}(s, {});".format(
+ cmd, param[1]))
+ elif cmd.startswith("out") or cmd.startswith("write"):
+ result.append("qtest_{}(s, {}, {});".format(
+ cmd, param[1], param[2]))
+ elif cmd == "clock_step":
+ if len(param) ==1:
+ result.append("qtest_clock_step_next(s);")
+ else:
+ result.append("qtest_clock_step(s, {});".format(param[1]))
+ result.append("qtest_quit(s);\n}")
+ return "\n".join(result)
+
+def c_main(name, arch):
+ return """int main(int argc, char **argv)
+{{
+ const char *arch = qtest_get_arch();
+
+ g_test_init(&argc, &argv, NULL);
+
+ if (strcmp(arch, "{arch}") == 0) {{
+ qtest_add_func("fuzz/{name}",{name});
+ }}
+
+ return g_test_run();
+}}""".format(name=name, arch=arch)
+
+def main():
+ parser = argparse.ArgumentParser()
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument("-bash", help="Only output a copy-pastable bash command",
+ action="store_true")
+ group.add_argument("-c", help="Only output a c function",
+ action="store_true")
+ parser.add_argument('-owner', help="If generating complete C source code, \
+ this specifies the Copyright owner",
+ nargs='?', default="<name of author>")
+ parser.add_argument("-no_comment", help="Don't include a bash reproducer \
+ as a comment in the C reproducers",
+ action="store_true")
+ parser.add_argument('-name', help="The name of the c function",
+ nargs='?', default="test_fuzz")
+ parser.add_argument('input_trace', help="input QTest command sequence \
+ (stdin by default)",
+ nargs='?', type=argparse.FileType('r'),
+ default=sys.stdin)
+ args = parser.parse_args()
+
+ qemu_path = os.getenv("QEMU_PATH")
+ qemu_args = os.getenv("QEMU_ARGS")
+ if not qemu_args or not qemu_path:
+ print("Please set QEMU_PATH and QEMU_ARGS environment variables")
+ sys.exit(1)
+
+ bash_args = qemu_args
+ if " -qtest stdio" not in qemu_args:
+ bash_args += " -qtest stdio"
+
+ arch = qemu_path.split("-")[-1]
+ trace = args.input_trace.read().strip()
+
+ if args.bash :
+ print(bash_reproducer(qemu_path, bash_args, trace))
+ else:
+ output = ""
+ if not args.c:
+ output += c_header(args.owner) + "\n"
+ if not args.no_comment:
+ output += c_comment(bash_reproducer(qemu_path, bash_args, trace))
+ output += c_reproducer(args.name, qemu_args, trace)
+ if not args.c:
+ output += c_main(args.name, arch)
+ print(output)
+
+
+if __name__ == '__main__':
+ main()
--
2.26.2
next prev parent reply other threads:[~2021-03-16 21:19 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-03-16 21:15 [PULL 00/16] Fuzzing + bugfix patches for QEMU 6.0 soft freeze Paolo Bonzini
2021-03-16 21:15 ` [PULL 01/16] tests/qtest: Only run fuzz-megasas-test if megasas device is available Paolo Bonzini
2021-03-16 21:15 ` [PULL 02/16] tests/qtest: Only run fuzz-virtio-scsi when virtio-scsi " Paolo Bonzini
2021-03-16 21:15 ` [PULL 03/16] MAINTAINERS: Cover fuzzer reproducer tests within 'Device Fuzzing' Paolo Bonzini
2021-03-16 21:15 ` [PULL 04/16] fuzz: fix the pro100 generic-fuzzer config Paolo Bonzini
2021-03-16 21:15 ` [PULL 05/16] fuzz: don't leave orphan llvm-symbolizers around Paolo Bonzini
2021-03-16 21:15 ` Paolo Bonzini [this message]
2021-03-16 21:15 ` [PULL 07/16] fuzz: add instructions for building reproducers Paolo Bonzini
2021-03-16 21:15 ` [PULL 08/16] fuzz: add a am53c974 generic-fuzzer config Paolo Bonzini
2021-03-16 21:15 ` [PULL 09/16] memory: add a sparse memory device for fuzzing Paolo Bonzini
2021-04-06 13:29 ` Philippe Mathieu-Daudé
2021-03-16 21:15 ` [PULL 10/16] fuzz: configure a sparse-mem device, by default Paolo Bonzini
2021-03-16 21:15 ` [PULL 11/16] fuzz: move some DMA hooks Paolo Bonzini
2021-03-16 21:15 ` [PULL 12/16] configure: add option to explicitly enable/disable libgio Paolo Bonzini
2021-03-16 21:15 ` [PULL 13/16] Revert "accel: kvm: Add aligment assert for kvm_log_clear_one_slot" Paolo Bonzini
2021-03-16 21:15 ` [PULL 14/16] scsi: fix sense code for EREMOTEIO Paolo Bonzini
2021-03-16 21:15 ` [PULL 15/16] hw/i8254: fix vmstate load Paolo Bonzini
2021-03-16 21:15 ` [PULL 16/16] qemu-timer: allow freeing a NULL timer Paolo Bonzini
2021-03-18 10:05 ` [PULL 00/16] Fuzzing + bugfix patches for QEMU 6.0 soft freeze Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210316211531.1649909-7-pbonzini@redhat.com \
--to=pbonzini@redhat.com \
--cc=alxndr@bu.edu \
--cc=darren.kenny@oracle.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).