cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

output_reproducer.py (5539B)


      1#!/usr/bin/env python3
      2# -*- coding: utf-8 -*-
      3
      4"""
      5Convert plain qtest traces to C or Bash reproducers
      6
      7Use this to help build bug-reports or create in-tree reproducers for bugs.
      8Note: This will not format C code for you. Pipe the output through
      9clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, ColumnLimit: 90}"
     10or similar
     11"""
     12
     13import sys
     14import os
     15import argparse
     16import textwrap
     17from datetime import date
     18
     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"
     22
     23__maintainer__ = "Alexander Bulekov"
     24__email__      = "alxndr@bu.edu"
     25
     26
     27def c_header(owner):
     28    return """/*
     29 * Autogenerated Fuzzer Test Case
     30 *
     31 * Copyright (c) {date} {owner}
     32 *
     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.
     35 */
     36
     37#include "qemu/osdep.h"
     38
     39#include "libqos/libqtest.h"
     40
     41    """.format(date=date.today().year, owner=owner)
     42
     43def c_comment(s):
     44    """ Return a multi-line C comment. Assume the text is already wrapped """
     45    return "/*\n * " + "\n * ".join(s.splitlines()) + "\n*/"
     46
     47def print_c_function(s):
     48    print("/* ")
     49    for l in s.splitlines():
     50        print(" * {}".format(l))
     51
     52def 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))
     58    result += "\nEOF"
     59    return result
     60
     61def c_reproducer(name, args, trace):
     62    result = []
     63    result.append("""static void {}(void)\n{{""".format(name))
     64
     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():
     72        param = l.split()
     73        cmd = param[0]
     74        if cmd == "write":
     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)
     79            addr = param[1]
     80            size = param[2]
     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(
     85                          cmd, param[1]))
     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":
     90            if len(param) ==1:
     91                result.append("qtest_clock_step_next(s);")
     92            else:
     93                result.append("qtest_clock_step(s, {});".format(param[1]))
     94    result.append("qtest_quit(s);\n}")
     95    return "\n".join(result)
     96
     97def c_main(name, arch):
     98    return """int main(int argc, char **argv)
     99{{
    100    const char *arch = qtest_get_arch();
    101
    102    g_test_init(&argc, &argv, NULL);
    103
    104   if (strcmp(arch, "{arch}") == 0) {{
    105        qtest_add_func("fuzz/{name}",{name});
    106   }}
    107
    108   return g_test_run();
    109}}""".format(name=name, arch=arch)
    110
    111def main():
    112    parser = argparse.ArgumentParser()
    113    group = parser.add_mutually_exclusive_group()
    114    group.add_argument("-bash", help="Only output a copy-pastable bash command",
    115                        action="store_true")
    116    group.add_argument("-c", help="Only output a c function",
    117                        action="store_true")
    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",
    123                        action="store_true")
    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 \
    127                        (stdin by default)",
    128                        nargs='?', type=argparse.FileType('r'),
    129                        default=sys.stdin)
    130    args = parser.parse_args()
    131
    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")
    136        sys.exit(1)
    137
    138    bash_args = qemu_args
    139    if " -qtest stdio" not in  qemu_args:
    140        bash_args += " -qtest stdio"
    141
    142    arch = qemu_path.split("-")[-1]
    143    trace = args.input_trace.read().strip()
    144
    145    if args.bash :
    146        print(bash_reproducer(qemu_path, bash_args, trace))
    147    else:
    148        output = ""
    149        if not args.c:
    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)
    154        if not args.c:
    155            output += c_main(args.name, arch)
    156        print(output)
    157
    158
    159if __name__ == '__main__':
    160    main()