197ef5f88SAlexander Bulekov#!/usr/bin/env python3 297ef5f88SAlexander Bulekov# -*- coding: utf-8 -*- 397ef5f88SAlexander Bulekov 497ef5f88SAlexander Bulekov""" 597ef5f88SAlexander BulekovConvert plain qtest traces to C or Bash reproducers 697ef5f88SAlexander Bulekov 797ef5f88SAlexander BulekovUse this to help build bug-reports or create in-tree reproducers for bugs. 897ef5f88SAlexander BulekovNote: This will not format C code for you. Pipe the output through 997ef5f88SAlexander Bulekovclang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, ColumnLimit: 90}" 1097ef5f88SAlexander Bulekovor similar 1197ef5f88SAlexander Bulekov""" 1297ef5f88SAlexander Bulekov 1397ef5f88SAlexander Bulekovimport sys 1497ef5f88SAlexander Bulekovimport os 1597ef5f88SAlexander Bulekovimport argparse 1697ef5f88SAlexander Bulekovimport textwrap 1797ef5f88SAlexander Bulekovfrom datetime import date 1897ef5f88SAlexander Bulekov 1997ef5f88SAlexander Bulekov__author__ = "Alexander Bulekov <alxndr@bu.edu>" 2097ef5f88SAlexander Bulekov__copyright__ = "Copyright (C) 2021, Red Hat, Inc." 2197ef5f88SAlexander Bulekov__license__ = "GPL version 2 or (at your option) any later version" 2297ef5f88SAlexander Bulekov 2397ef5f88SAlexander Bulekov__maintainer__ = "Alexander Bulekov" 2497ef5f88SAlexander Bulekov__email__ = "alxndr@bu.edu" 2597ef5f88SAlexander Bulekov 2697ef5f88SAlexander Bulekov 2797ef5f88SAlexander Bulekovdef c_header(owner): 2897ef5f88SAlexander Bulekov return """/* 2997ef5f88SAlexander Bulekov * Autogenerated Fuzzer Test Case 3097ef5f88SAlexander Bulekov * 3197ef5f88SAlexander Bulekov * Copyright (c) {date} {owner} 3297ef5f88SAlexander Bulekov * 3397ef5f88SAlexander Bulekov * This work is licensed under the terms of the GNU GPL, version 2 or later. 3497ef5f88SAlexander Bulekov * See the COPYING file in the top-level directory. 3597ef5f88SAlexander Bulekov */ 3697ef5f88SAlexander Bulekov 3797ef5f88SAlexander Bulekov#include "qemu/osdep.h" 3897ef5f88SAlexander Bulekov 39*907b5105SMarc-André Lureau#include "libqtest.h" 4097ef5f88SAlexander Bulekov 4197ef5f88SAlexander Bulekov """.format(date=date.today().year, owner=owner) 4297ef5f88SAlexander Bulekov 4397ef5f88SAlexander Bulekovdef c_comment(s): 4497ef5f88SAlexander Bulekov """ Return a multi-line C comment. Assume the text is already wrapped """ 4597ef5f88SAlexander Bulekov return "/*\n * " + "\n * ".join(s.splitlines()) + "\n*/" 4697ef5f88SAlexander Bulekov 4797ef5f88SAlexander Bulekovdef print_c_function(s): 4897ef5f88SAlexander Bulekov print("/* ") 4997ef5f88SAlexander Bulekov for l in s.splitlines(): 5097ef5f88SAlexander Bulekov print(" * {}".format(l)) 5197ef5f88SAlexander Bulekov 5297ef5f88SAlexander Bulekovdef bash_reproducer(path, args, trace): 5397ef5f88SAlexander Bulekov result = '\\\n'.join(textwrap.wrap("cat << EOF | {} {}".format(path, args), 5497ef5f88SAlexander Bulekov 72, break_on_hyphens=False, 5597ef5f88SAlexander Bulekov drop_whitespace=False)) 5697ef5f88SAlexander Bulekov for l in trace.splitlines(): 5797ef5f88SAlexander Bulekov result += "\n" + '\\\n'.join(textwrap.wrap(l,72,drop_whitespace=False)) 5897ef5f88SAlexander Bulekov result += "\nEOF" 5997ef5f88SAlexander Bulekov return result 6097ef5f88SAlexander Bulekov 6197ef5f88SAlexander Bulekovdef c_reproducer(name, args, trace): 6297ef5f88SAlexander Bulekov result = [] 6397ef5f88SAlexander Bulekov result.append("""static void {}(void)\n{{""".format(name)) 6497ef5f88SAlexander Bulekov 6597ef5f88SAlexander Bulekov # libqtest will add its own qtest args, so get rid of them 6697ef5f88SAlexander Bulekov args = args.replace("-accel qtest","") 6797ef5f88SAlexander Bulekov args = args.replace(",accel=qtest","") 6897ef5f88SAlexander Bulekov args = args.replace("-machine accel=qtest","") 6997ef5f88SAlexander Bulekov args = args.replace("-qtest stdio","") 7097ef5f88SAlexander Bulekov result.append("""QTestState *s = qtest_init("{}");""".format(args)) 7197ef5f88SAlexander Bulekov for l in trace.splitlines(): 7297ef5f88SAlexander Bulekov param = l.split() 7397ef5f88SAlexander Bulekov cmd = param[0] 7497ef5f88SAlexander Bulekov if cmd == "write": 7597ef5f88SAlexander Bulekov buf = param[3][2:] #Get the 0x... buffer and trim the "0x" 7697ef5f88SAlexander Bulekov assert len(buf)%2 == 0 7797ef5f88SAlexander Bulekov bufbytes = [buf[i:i+2] for i in range(0, len(buf), 2)] 7897ef5f88SAlexander Bulekov bufstring = '\\x'+'\\x'.join(bufbytes) 7997ef5f88SAlexander Bulekov addr = param[1] 8097ef5f88SAlexander Bulekov size = param[2] 8197ef5f88SAlexander Bulekov result.append("""qtest_bufwrite(s, {}, "{}", {});""".format( 8297ef5f88SAlexander Bulekov addr, bufstring, size)) 8397ef5f88SAlexander Bulekov elif cmd.startswith("in") or cmd.startswith("read"): 8497ef5f88SAlexander Bulekov result.append("qtest_{}(s, {});".format( 8597ef5f88SAlexander Bulekov cmd, param[1])) 8697ef5f88SAlexander Bulekov elif cmd.startswith("out") or cmd.startswith("write"): 8797ef5f88SAlexander Bulekov result.append("qtest_{}(s, {}, {});".format( 8897ef5f88SAlexander Bulekov cmd, param[1], param[2])) 8997ef5f88SAlexander Bulekov elif cmd == "clock_step": 9097ef5f88SAlexander Bulekov if len(param) ==1: 9197ef5f88SAlexander Bulekov result.append("qtest_clock_step_next(s);") 9297ef5f88SAlexander Bulekov else: 9397ef5f88SAlexander Bulekov result.append("qtest_clock_step(s, {});".format(param[1])) 9497ef5f88SAlexander Bulekov result.append("qtest_quit(s);\n}") 9597ef5f88SAlexander Bulekov return "\n".join(result) 9697ef5f88SAlexander Bulekov 9797ef5f88SAlexander Bulekovdef c_main(name, arch): 9897ef5f88SAlexander Bulekov return """int main(int argc, char **argv) 9997ef5f88SAlexander Bulekov{{ 10097ef5f88SAlexander Bulekov const char *arch = qtest_get_arch(); 10197ef5f88SAlexander Bulekov 10297ef5f88SAlexander Bulekov g_test_init(&argc, &argv, NULL); 10397ef5f88SAlexander Bulekov 10497ef5f88SAlexander Bulekov if (strcmp(arch, "{arch}") == 0) {{ 10597ef5f88SAlexander Bulekov qtest_add_func("fuzz/{name}",{name}); 10697ef5f88SAlexander Bulekov }} 10797ef5f88SAlexander Bulekov 10897ef5f88SAlexander Bulekov return g_test_run(); 10997ef5f88SAlexander Bulekov}}""".format(name=name, arch=arch) 11097ef5f88SAlexander Bulekov 11197ef5f88SAlexander Bulekovdef main(): 11297ef5f88SAlexander Bulekov parser = argparse.ArgumentParser() 11397ef5f88SAlexander Bulekov group = parser.add_mutually_exclusive_group() 11497ef5f88SAlexander Bulekov group.add_argument("-bash", help="Only output a copy-pastable bash command", 11597ef5f88SAlexander Bulekov action="store_true") 11697ef5f88SAlexander Bulekov group.add_argument("-c", help="Only output a c function", 11797ef5f88SAlexander Bulekov action="store_true") 11897ef5f88SAlexander Bulekov parser.add_argument('-owner', help="If generating complete C source code, \ 11997ef5f88SAlexander Bulekov this specifies the Copyright owner", 12097ef5f88SAlexander Bulekov nargs='?', default="<name of author>") 12197ef5f88SAlexander Bulekov parser.add_argument("-no_comment", help="Don't include a bash reproducer \ 12297ef5f88SAlexander Bulekov as a comment in the C reproducers", 12397ef5f88SAlexander Bulekov action="store_true") 12497ef5f88SAlexander Bulekov parser.add_argument('-name', help="The name of the c function", 12597ef5f88SAlexander Bulekov nargs='?', default="test_fuzz") 12697ef5f88SAlexander Bulekov parser.add_argument('input_trace', help="input QTest command sequence \ 12797ef5f88SAlexander Bulekov (stdin by default)", 12897ef5f88SAlexander Bulekov nargs='?', type=argparse.FileType('r'), 12997ef5f88SAlexander Bulekov default=sys.stdin) 13097ef5f88SAlexander Bulekov args = parser.parse_args() 13197ef5f88SAlexander Bulekov 13297ef5f88SAlexander Bulekov qemu_path = os.getenv("QEMU_PATH") 13397ef5f88SAlexander Bulekov qemu_args = os.getenv("QEMU_ARGS") 13497ef5f88SAlexander Bulekov if not qemu_args or not qemu_path: 13597ef5f88SAlexander Bulekov print("Please set QEMU_PATH and QEMU_ARGS environment variables") 13697ef5f88SAlexander Bulekov sys.exit(1) 13797ef5f88SAlexander Bulekov 13897ef5f88SAlexander Bulekov bash_args = qemu_args 13997ef5f88SAlexander Bulekov if " -qtest stdio" not in qemu_args: 14097ef5f88SAlexander Bulekov bash_args += " -qtest stdio" 14197ef5f88SAlexander Bulekov 14297ef5f88SAlexander Bulekov arch = qemu_path.split("-")[-1] 14397ef5f88SAlexander Bulekov trace = args.input_trace.read().strip() 14497ef5f88SAlexander Bulekov 14597ef5f88SAlexander Bulekov if args.bash : 14697ef5f88SAlexander Bulekov print(bash_reproducer(qemu_path, bash_args, trace)) 14797ef5f88SAlexander Bulekov else: 14897ef5f88SAlexander Bulekov output = "" 14997ef5f88SAlexander Bulekov if not args.c: 15097ef5f88SAlexander Bulekov output += c_header(args.owner) + "\n" 15197ef5f88SAlexander Bulekov if not args.no_comment: 15297ef5f88SAlexander Bulekov output += c_comment(bash_reproducer(qemu_path, bash_args, trace)) 15397ef5f88SAlexander Bulekov output += c_reproducer(args.name, qemu_args, trace) 15497ef5f88SAlexander Bulekov if not args.c: 15597ef5f88SAlexander Bulekov output += c_main(args.name, arch) 15697ef5f88SAlexander Bulekov print(output) 15797ef5f88SAlexander Bulekov 15897ef5f88SAlexander Bulekov 15997ef5f88SAlexander Bulekovif __name__ == '__main__': 16097ef5f88SAlexander Bulekov main() 161