1*97ef5f88SAlexander Bulekov#!/usr/bin/env python3 2*97ef5f88SAlexander Bulekov# -*- coding: utf-8 -*- 3*97ef5f88SAlexander Bulekov 4*97ef5f88SAlexander Bulekov""" 5*97ef5f88SAlexander BulekovConvert plain qtest traces to C or Bash reproducers 6*97ef5f88SAlexander Bulekov 7*97ef5f88SAlexander BulekovUse this to help build bug-reports or create in-tree reproducers for bugs. 8*97ef5f88SAlexander BulekovNote: This will not format C code for you. Pipe the output through 9*97ef5f88SAlexander Bulekovclang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, ColumnLimit: 90}" 10*97ef5f88SAlexander Bulekovor similar 11*97ef5f88SAlexander Bulekov""" 12*97ef5f88SAlexander Bulekov 13*97ef5f88SAlexander Bulekovimport sys 14*97ef5f88SAlexander Bulekovimport os 15*97ef5f88SAlexander Bulekovimport argparse 16*97ef5f88SAlexander Bulekovimport textwrap 17*97ef5f88SAlexander Bulekovfrom datetime import date 18*97ef5f88SAlexander Bulekov 19*97ef5f88SAlexander Bulekov__author__ = "Alexander Bulekov <alxndr@bu.edu>" 20*97ef5f88SAlexander Bulekov__copyright__ = "Copyright (C) 2021, Red Hat, Inc." 21*97ef5f88SAlexander Bulekov__license__ = "GPL version 2 or (at your option) any later version" 22*97ef5f88SAlexander Bulekov 23*97ef5f88SAlexander Bulekov__maintainer__ = "Alexander Bulekov" 24*97ef5f88SAlexander Bulekov__email__ = "alxndr@bu.edu" 25*97ef5f88SAlexander Bulekov 26*97ef5f88SAlexander Bulekov 27*97ef5f88SAlexander Bulekovdef c_header(owner): 28*97ef5f88SAlexander Bulekov return """/* 29*97ef5f88SAlexander Bulekov * Autogenerated Fuzzer Test Case 30*97ef5f88SAlexander Bulekov * 31*97ef5f88SAlexander Bulekov * Copyright (c) {date} {owner} 32*97ef5f88SAlexander Bulekov * 33*97ef5f88SAlexander Bulekov * This work is licensed under the terms of the GNU GPL, version 2 or later. 34*97ef5f88SAlexander Bulekov * See the COPYING file in the top-level directory. 35*97ef5f88SAlexander Bulekov */ 36*97ef5f88SAlexander Bulekov 37*97ef5f88SAlexander Bulekov#include "qemu/osdep.h" 38*97ef5f88SAlexander Bulekov 39*97ef5f88SAlexander Bulekov#include "libqos/libqtest.h" 40*97ef5f88SAlexander Bulekov 41*97ef5f88SAlexander Bulekov """.format(date=date.today().year, owner=owner) 42*97ef5f88SAlexander Bulekov 43*97ef5f88SAlexander Bulekovdef c_comment(s): 44*97ef5f88SAlexander Bulekov """ Return a multi-line C comment. Assume the text is already wrapped """ 45*97ef5f88SAlexander Bulekov return "/*\n * " + "\n * ".join(s.splitlines()) + "\n*/" 46*97ef5f88SAlexander Bulekov 47*97ef5f88SAlexander Bulekovdef print_c_function(s): 48*97ef5f88SAlexander Bulekov print("/* ") 49*97ef5f88SAlexander Bulekov for l in s.splitlines(): 50*97ef5f88SAlexander Bulekov print(" * {}".format(l)) 51*97ef5f88SAlexander Bulekov 52*97ef5f88SAlexander Bulekovdef bash_reproducer(path, args, trace): 53*97ef5f88SAlexander Bulekov result = '\\\n'.join(textwrap.wrap("cat << EOF | {} {}".format(path, args), 54*97ef5f88SAlexander Bulekov 72, break_on_hyphens=False, 55*97ef5f88SAlexander Bulekov drop_whitespace=False)) 56*97ef5f88SAlexander Bulekov for l in trace.splitlines(): 57*97ef5f88SAlexander Bulekov result += "\n" + '\\\n'.join(textwrap.wrap(l,72,drop_whitespace=False)) 58*97ef5f88SAlexander Bulekov result += "\nEOF" 59*97ef5f88SAlexander Bulekov return result 60*97ef5f88SAlexander Bulekov 61*97ef5f88SAlexander Bulekovdef c_reproducer(name, args, trace): 62*97ef5f88SAlexander Bulekov result = [] 63*97ef5f88SAlexander Bulekov result.append("""static void {}(void)\n{{""".format(name)) 64*97ef5f88SAlexander Bulekov 65*97ef5f88SAlexander Bulekov # libqtest will add its own qtest args, so get rid of them 66*97ef5f88SAlexander Bulekov args = args.replace("-accel qtest","") 67*97ef5f88SAlexander Bulekov args = args.replace(",accel=qtest","") 68*97ef5f88SAlexander Bulekov args = args.replace("-machine accel=qtest","") 69*97ef5f88SAlexander Bulekov args = args.replace("-qtest stdio","") 70*97ef5f88SAlexander Bulekov result.append("""QTestState *s = qtest_init("{}");""".format(args)) 71*97ef5f88SAlexander Bulekov for l in trace.splitlines(): 72*97ef5f88SAlexander Bulekov param = l.split() 73*97ef5f88SAlexander Bulekov cmd = param[0] 74*97ef5f88SAlexander Bulekov if cmd == "write": 75*97ef5f88SAlexander Bulekov buf = param[3][2:] #Get the 0x... buffer and trim the "0x" 76*97ef5f88SAlexander Bulekov assert len(buf)%2 == 0 77*97ef5f88SAlexander Bulekov bufbytes = [buf[i:i+2] for i in range(0, len(buf), 2)] 78*97ef5f88SAlexander Bulekov bufstring = '\\x'+'\\x'.join(bufbytes) 79*97ef5f88SAlexander Bulekov addr = param[1] 80*97ef5f88SAlexander Bulekov size = param[2] 81*97ef5f88SAlexander Bulekov result.append("""qtest_bufwrite(s, {}, "{}", {});""".format( 82*97ef5f88SAlexander Bulekov addr, bufstring, size)) 83*97ef5f88SAlexander Bulekov elif cmd.startswith("in") or cmd.startswith("read"): 84*97ef5f88SAlexander Bulekov result.append("qtest_{}(s, {});".format( 85*97ef5f88SAlexander Bulekov cmd, param[1])) 86*97ef5f88SAlexander Bulekov elif cmd.startswith("out") or cmd.startswith("write"): 87*97ef5f88SAlexander Bulekov result.append("qtest_{}(s, {}, {});".format( 88*97ef5f88SAlexander Bulekov cmd, param[1], param[2])) 89*97ef5f88SAlexander Bulekov elif cmd == "clock_step": 90*97ef5f88SAlexander Bulekov if len(param) ==1: 91*97ef5f88SAlexander Bulekov result.append("qtest_clock_step_next(s);") 92*97ef5f88SAlexander Bulekov else: 93*97ef5f88SAlexander Bulekov result.append("qtest_clock_step(s, {});".format(param[1])) 94*97ef5f88SAlexander Bulekov result.append("qtest_quit(s);\n}") 95*97ef5f88SAlexander Bulekov return "\n".join(result) 96*97ef5f88SAlexander Bulekov 97*97ef5f88SAlexander Bulekovdef c_main(name, arch): 98*97ef5f88SAlexander Bulekov return """int main(int argc, char **argv) 99*97ef5f88SAlexander Bulekov{{ 100*97ef5f88SAlexander Bulekov const char *arch = qtest_get_arch(); 101*97ef5f88SAlexander Bulekov 102*97ef5f88SAlexander Bulekov g_test_init(&argc, &argv, NULL); 103*97ef5f88SAlexander Bulekov 104*97ef5f88SAlexander Bulekov if (strcmp(arch, "{arch}") == 0) {{ 105*97ef5f88SAlexander Bulekov qtest_add_func("fuzz/{name}",{name}); 106*97ef5f88SAlexander Bulekov }} 107*97ef5f88SAlexander Bulekov 108*97ef5f88SAlexander Bulekov return g_test_run(); 109*97ef5f88SAlexander Bulekov}}""".format(name=name, arch=arch) 110*97ef5f88SAlexander Bulekov 111*97ef5f88SAlexander Bulekovdef main(): 112*97ef5f88SAlexander Bulekov parser = argparse.ArgumentParser() 113*97ef5f88SAlexander Bulekov group = parser.add_mutually_exclusive_group() 114*97ef5f88SAlexander Bulekov group.add_argument("-bash", help="Only output a copy-pastable bash command", 115*97ef5f88SAlexander Bulekov action="store_true") 116*97ef5f88SAlexander Bulekov group.add_argument("-c", help="Only output a c function", 117*97ef5f88SAlexander Bulekov action="store_true") 118*97ef5f88SAlexander Bulekov parser.add_argument('-owner', help="If generating complete C source code, \ 119*97ef5f88SAlexander Bulekov this specifies the Copyright owner", 120*97ef5f88SAlexander Bulekov nargs='?', default="<name of author>") 121*97ef5f88SAlexander Bulekov parser.add_argument("-no_comment", help="Don't include a bash reproducer \ 122*97ef5f88SAlexander Bulekov as a comment in the C reproducers", 123*97ef5f88SAlexander Bulekov action="store_true") 124*97ef5f88SAlexander Bulekov parser.add_argument('-name', help="The name of the c function", 125*97ef5f88SAlexander Bulekov nargs='?', default="test_fuzz") 126*97ef5f88SAlexander Bulekov parser.add_argument('input_trace', help="input QTest command sequence \ 127*97ef5f88SAlexander Bulekov (stdin by default)", 128*97ef5f88SAlexander Bulekov nargs='?', type=argparse.FileType('r'), 129*97ef5f88SAlexander Bulekov default=sys.stdin) 130*97ef5f88SAlexander Bulekov args = parser.parse_args() 131*97ef5f88SAlexander Bulekov 132*97ef5f88SAlexander Bulekov qemu_path = os.getenv("QEMU_PATH") 133*97ef5f88SAlexander Bulekov qemu_args = os.getenv("QEMU_ARGS") 134*97ef5f88SAlexander Bulekov if not qemu_args or not qemu_path: 135*97ef5f88SAlexander Bulekov print("Please set QEMU_PATH and QEMU_ARGS environment variables") 136*97ef5f88SAlexander Bulekov sys.exit(1) 137*97ef5f88SAlexander Bulekov 138*97ef5f88SAlexander Bulekov bash_args = qemu_args 139*97ef5f88SAlexander Bulekov if " -qtest stdio" not in qemu_args: 140*97ef5f88SAlexander Bulekov bash_args += " -qtest stdio" 141*97ef5f88SAlexander Bulekov 142*97ef5f88SAlexander Bulekov arch = qemu_path.split("-")[-1] 143*97ef5f88SAlexander Bulekov trace = args.input_trace.read().strip() 144*97ef5f88SAlexander Bulekov 145*97ef5f88SAlexander Bulekov if args.bash : 146*97ef5f88SAlexander Bulekov print(bash_reproducer(qemu_path, bash_args, trace)) 147*97ef5f88SAlexander Bulekov else: 148*97ef5f88SAlexander Bulekov output = "" 149*97ef5f88SAlexander Bulekov if not args.c: 150*97ef5f88SAlexander Bulekov output += c_header(args.owner) + "\n" 151*97ef5f88SAlexander Bulekov if not args.no_comment: 152*97ef5f88SAlexander Bulekov output += c_comment(bash_reproducer(qemu_path, bash_args, trace)) 153*97ef5f88SAlexander Bulekov output += c_reproducer(args.name, qemu_args, trace) 154*97ef5f88SAlexander Bulekov if not args.c: 155*97ef5f88SAlexander Bulekov output += c_main(args.name, arch) 156*97ef5f88SAlexander Bulekov print(output) 157*97ef5f88SAlexander Bulekov 158*97ef5f88SAlexander Bulekov 159*97ef5f88SAlexander Bulekovif __name__ == '__main__': 160*97ef5f88SAlexander Bulekov main() 161