1*7c9b64adSAlexander Bulekov#!/usr/bin/env python3 2*7c9b64adSAlexander Bulekov# -*- coding: utf-8 -*- 3*7c9b64adSAlexander Bulekov 4*7c9b64adSAlexander Bulekov""" 5*7c9b64adSAlexander BulekovUse this to convert qtest log info from a generic fuzzer input into a qtest 6*7c9b64adSAlexander Bulekovtrace that you can feed into a standard qemu-system process. Example usage: 7*7c9b64adSAlexander Bulekov 8*7c9b64adSAlexander BulekovQEMU_FUZZ_ARGS="-machine q35,accel=qtest" QEMU_FUZZ_OBJECTS="*" \ 9*7c9b64adSAlexander Bulekov ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=generic-pci-fuzz 10*7c9b64adSAlexander Bulekov# .. Finds some crash 11*7c9b64adSAlexander BulekovQTEST_LOG=1 FUZZ_SERIALIZE_QTEST=1 \ 12*7c9b64adSAlexander BulekovQEMU_FUZZ_ARGS="-machine q35,accel=qtest" QEMU_FUZZ_OBJECTS="*" \ 13*7c9b64adSAlexander Bulekov ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=generic-pci-fuzz 14*7c9b64adSAlexander Bulekov /path/to/crash 2> qtest_log_output 15*7c9b64adSAlexander Bulekovscripts/oss-fuzz/reorder_fuzzer_qtest_trace.py qtest_log_output > qtest_trace 16*7c9b64adSAlexander Bulekov./i386-softmmu/qemu-fuzz-i386 -machine q35,accel=qtest \ 17*7c9b64adSAlexander Bulekov -qtest stdin < qtest_trace 18*7c9b64adSAlexander Bulekov 19*7c9b64adSAlexander Bulekov### Details ### 20*7c9b64adSAlexander Bulekov 21*7c9b64adSAlexander BulekovSome fuzzer make use of hooks that allow us to populate some memory range, just 22*7c9b64adSAlexander Bulekovbefore a DMA read from that range. This means that the fuzzer can produce 23*7c9b64adSAlexander Bulekovactivity that looks like: 24*7c9b64adSAlexander Bulekov [start] read from mmio addr 25*7c9b64adSAlexander Bulekov [end] read from mmio addr 26*7c9b64adSAlexander Bulekov [start] write to pio addr 27*7c9b64adSAlexander Bulekov [start] fill a DMA buffer just in time 28*7c9b64adSAlexander Bulekov [end] fill a DMA buffer just in time 29*7c9b64adSAlexander Bulekov [start] fill a DMA buffer just in time 30*7c9b64adSAlexander Bulekov [end] fill a DMA buffer just in time 31*7c9b64adSAlexander Bulekov [end] write to pio addr 32*7c9b64adSAlexander Bulekov [start] read from mmio addr 33*7c9b64adSAlexander Bulekov [end] read from mmio addr 34*7c9b64adSAlexander Bulekov 35*7c9b64adSAlexander BulekovWe annotate these "nested" DMA writes, so with QTEST_LOG=1 the QTest trace 36*7c9b64adSAlexander Bulekovmight look something like: 37*7c9b64adSAlexander Bulekov[R +0.028431] readw 0x10000 38*7c9b64adSAlexander Bulekov[R +0.028434] outl 0xc000 0xbeef # Triggers a DMA read from 0xbeef and 0xbf00 39*7c9b64adSAlexander Bulekov[DMA][R +0.034639] write 0xbeef 0x2 0xAAAA 40*7c9b64adSAlexander Bulekov[DMA][R +0.034639] write 0xbf00 0x2 0xBBBB 41*7c9b64adSAlexander Bulekov[R +0.028431] readw 0xfc000 42*7c9b64adSAlexander Bulekov 43*7c9b64adSAlexander BulekovThis script would reorder the above trace so it becomes: 44*7c9b64adSAlexander Bulekovreadw 0x10000 45*7c9b64adSAlexander Bulekovwrite 0xbeef 0x2 0xAAAA 46*7c9b64adSAlexander Bulekovwrite 0xbf00 0x2 0xBBBB 47*7c9b64adSAlexander Bulekovoutl 0xc000 0xbeef 48*7c9b64adSAlexander Bulekovreadw 0xfc000 49*7c9b64adSAlexander Bulekov 50*7c9b64adSAlexander BulekovI.e. by the time, 0xc000 tries to read from DMA, those DMA buffers have already 51*7c9b64adSAlexander Bulekovbeen set up, removing the need for the DMA hooks. We can simply provide this 52*7c9b64adSAlexander Bulekovreordered trace via -qtest stdio to reproduce the input 53*7c9b64adSAlexander Bulekov 54*7c9b64adSAlexander BulekovNote: this won't work for traces where the device tries to read from the same 55*7c9b64adSAlexander BulekovDMA region twice in between MMIO/PIO commands. E.g: 56*7c9b64adSAlexander Bulekov [R +0.028434] outl 0xc000 0xbeef 57*7c9b64adSAlexander Bulekov [DMA][R +0.034639] write 0xbeef 0x2 0xAAAA 58*7c9b64adSAlexander Bulekov [DMA][R +0.034639] write 0xbeef 0x2 0xBBBB 59*7c9b64adSAlexander Bulekov 60*7c9b64adSAlexander BulekovThe fuzzer will annotate suspected double-fetches with [DOUBLE-FETCH]. This 61*7c9b64adSAlexander Bulekovscript looks for these tags and warns the users that the resulting trace might 62*7c9b64adSAlexander Bulekovnot reproduce the bug. 63*7c9b64adSAlexander Bulekov""" 64*7c9b64adSAlexander Bulekov 65*7c9b64adSAlexander Bulekovimport sys 66*7c9b64adSAlexander Bulekov 67*7c9b64adSAlexander Bulekov__author__ = "Alexander Bulekov <alxndr@bu.edu>" 68*7c9b64adSAlexander Bulekov__copyright__ = "Copyright (C) 2020, Red Hat, Inc." 69*7c9b64adSAlexander Bulekov__license__ = "GPL version 2 or (at your option) any later version" 70*7c9b64adSAlexander Bulekov 71*7c9b64adSAlexander Bulekov__maintainer__ = "Alexander Bulekov" 72*7c9b64adSAlexander Bulekov__email__ = "alxndr@bu.edu" 73*7c9b64adSAlexander Bulekov 74*7c9b64adSAlexander Bulekov 75*7c9b64adSAlexander Bulekovdef usage(): 76*7c9b64adSAlexander Bulekov sys.exit("Usage: {} /path/to/qtest_log_output".format((sys.argv[0]))) 77*7c9b64adSAlexander Bulekov 78*7c9b64adSAlexander Bulekov 79*7c9b64adSAlexander Bulekovdef main(filename): 80*7c9b64adSAlexander Bulekov with open(filename, "r") as f: 81*7c9b64adSAlexander Bulekov trace = f.readlines() 82*7c9b64adSAlexander Bulekov 83*7c9b64adSAlexander Bulekov # Leave only lines that look like logged qtest commands 84*7c9b64adSAlexander Bulekov trace[:] = [x.strip() for x in trace if "[R +" in x 85*7c9b64adSAlexander Bulekov or "[S +" in x and "CLOSED" not in x] 86*7c9b64adSAlexander Bulekov 87*7c9b64adSAlexander Bulekov for i in range(len(trace)): 88*7c9b64adSAlexander Bulekov if i+1 < len(trace): 89*7c9b64adSAlexander Bulekov if "[DMA]" in trace[i+1]: 90*7c9b64adSAlexander Bulekov if "[DOUBLE-FETCH]" in trace[i+1]: 91*7c9b64adSAlexander Bulekov sys.stderr.write("Warning: Likely double fetch on line" 92*7c9b64adSAlexander Bulekov "{}.\n There will likely be problems " 93*7c9b64adSAlexander Bulekov "reproducing behavior with the " 94*7c9b64adSAlexander Bulekov "resulting qtest trace\n\n".format(i+1)) 95*7c9b64adSAlexander Bulekov trace[i], trace[i+1] = trace[i+1], trace[i] 96*7c9b64adSAlexander Bulekov for line in trace: 97*7c9b64adSAlexander Bulekov print(line.split("]")[-1].strip()) 98*7c9b64adSAlexander Bulekov 99*7c9b64adSAlexander Bulekov 100*7c9b64adSAlexander Bulekovif __name__ == '__main__': 101*7c9b64adSAlexander Bulekov if len(sys.argv) == 1: 102*7c9b64adSAlexander Bulekov usage() 103*7c9b64adSAlexander Bulekov main(sys.argv[1]) 104