xref: /qemu/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py (revision 33ba8b0adc91482dd4247a0773cfe7def011933f)
17c9b64adSAlexander Bulekov#!/usr/bin/env python3
27c9b64adSAlexander Bulekov# -*- coding: utf-8 -*-
37c9b64adSAlexander Bulekov
47c9b64adSAlexander Bulekov"""
57c9b64adSAlexander BulekovUse this to convert qtest log info from a generic fuzzer input into a qtest
67c9b64adSAlexander Bulekovtrace that you can feed into a standard qemu-system process. Example usage:
77c9b64adSAlexander Bulekov
87c9b64adSAlexander BulekovQEMU_FUZZ_ARGS="-machine q35,accel=qtest" QEMU_FUZZ_OBJECTS="*" \
97c9b64adSAlexander Bulekov        ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=generic-pci-fuzz
107c9b64adSAlexander Bulekov# .. Finds some crash
117c9b64adSAlexander BulekovQTEST_LOG=1 FUZZ_SERIALIZE_QTEST=1 \
127c9b64adSAlexander BulekovQEMU_FUZZ_ARGS="-machine q35,accel=qtest" QEMU_FUZZ_OBJECTS="*" \
137c9b64adSAlexander Bulekov        ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=generic-pci-fuzz
147c9b64adSAlexander Bulekov        /path/to/crash 2> qtest_log_output
157c9b64adSAlexander Bulekovscripts/oss-fuzz/reorder_fuzzer_qtest_trace.py qtest_log_output > qtest_trace
167c9b64adSAlexander Bulekov./i386-softmmu/qemu-fuzz-i386 -machine q35,accel=qtest \
17*eeae5466SPhilippe Mathieu-Daudé        -qtest stdio < qtest_trace
187c9b64adSAlexander Bulekov
197c9b64adSAlexander Bulekov### Details ###
207c9b64adSAlexander Bulekov
217c9b64adSAlexander BulekovSome fuzzer make use of hooks that allow us to populate some memory range, just
227c9b64adSAlexander Bulekovbefore a DMA read from that range. This means that the fuzzer can produce
237c9b64adSAlexander Bulekovactivity that looks like:
247c9b64adSAlexander Bulekov    [start] read from mmio addr
257c9b64adSAlexander Bulekov    [end]   read from mmio addr
267c9b64adSAlexander Bulekov    [start] write to pio addr
277c9b64adSAlexander Bulekov        [start] fill a DMA buffer just in time
287c9b64adSAlexander Bulekov        [end]   fill a DMA buffer just in time
297c9b64adSAlexander Bulekov        [start] fill a DMA buffer just in time
307c9b64adSAlexander Bulekov        [end]   fill a DMA buffer just in time
317c9b64adSAlexander Bulekov    [end]   write to pio addr
327c9b64adSAlexander Bulekov    [start] read from mmio addr
337c9b64adSAlexander Bulekov    [end]   read from mmio addr
347c9b64adSAlexander Bulekov
357c9b64adSAlexander BulekovWe annotate these "nested" DMA writes, so with QTEST_LOG=1 the QTest trace
367c9b64adSAlexander Bulekovmight look something like:
377c9b64adSAlexander Bulekov[R +0.028431] readw 0x10000
387c9b64adSAlexander Bulekov[R +0.028434] outl 0xc000 0xbeef  # Triggers a DMA read from 0xbeef and 0xbf00
397c9b64adSAlexander Bulekov[DMA][R +0.034639] write 0xbeef 0x2 0xAAAA
407c9b64adSAlexander Bulekov[DMA][R +0.034639] write 0xbf00 0x2 0xBBBB
417c9b64adSAlexander Bulekov[R +0.028431] readw 0xfc000
427c9b64adSAlexander Bulekov
437c9b64adSAlexander BulekovThis script would reorder the above trace so it becomes:
447c9b64adSAlexander Bulekovreadw 0x10000
457c9b64adSAlexander Bulekovwrite 0xbeef 0x2 0xAAAA
467c9b64adSAlexander Bulekovwrite 0xbf00 0x2 0xBBBB
477c9b64adSAlexander Bulekovoutl 0xc000 0xbeef
487c9b64adSAlexander Bulekovreadw 0xfc000
497c9b64adSAlexander Bulekov
507c9b64adSAlexander BulekovI.e. by the time, 0xc000 tries to read from DMA, those DMA buffers have already
517c9b64adSAlexander Bulekovbeen set up, removing the need for the DMA hooks. We can simply provide this
527c9b64adSAlexander Bulekovreordered trace via -qtest stdio to reproduce the input
537c9b64adSAlexander Bulekov
547c9b64adSAlexander BulekovNote: this won't work for traces where the device tries to read from the same
557c9b64adSAlexander BulekovDMA region twice in between MMIO/PIO commands. E.g:
567c9b64adSAlexander Bulekov    [R +0.028434] outl 0xc000 0xbeef
577c9b64adSAlexander Bulekov    [DMA][R +0.034639] write 0xbeef 0x2 0xAAAA
587c9b64adSAlexander Bulekov    [DMA][R +0.034639] write 0xbeef 0x2 0xBBBB
597c9b64adSAlexander Bulekov
607c9b64adSAlexander BulekovThe fuzzer will annotate suspected double-fetches with [DOUBLE-FETCH]. This
617c9b64adSAlexander Bulekovscript looks for these tags and warns the users that the resulting trace might
627c9b64adSAlexander Bulekovnot reproduce the bug.
637c9b64adSAlexander Bulekov"""
647c9b64adSAlexander Bulekov
657c9b64adSAlexander Bulekovimport sys
667c9b64adSAlexander Bulekov
677c9b64adSAlexander Bulekov__author__     = "Alexander Bulekov <alxndr@bu.edu>"
687c9b64adSAlexander Bulekov__copyright__  = "Copyright (C) 2020, Red Hat, Inc."
697c9b64adSAlexander Bulekov__license__    = "GPL version 2 or (at your option) any later version"
707c9b64adSAlexander Bulekov
717c9b64adSAlexander Bulekov__maintainer__ = "Alexander Bulekov"
727c9b64adSAlexander Bulekov__email__      = "alxndr@bu.edu"
737c9b64adSAlexander Bulekov
747c9b64adSAlexander Bulekov
757c9b64adSAlexander Bulekovdef usage():
767c9b64adSAlexander Bulekov    sys.exit("Usage: {} /path/to/qtest_log_output".format((sys.argv[0])))
777c9b64adSAlexander Bulekov
787c9b64adSAlexander Bulekov
797c9b64adSAlexander Bulekovdef main(filename):
807c9b64adSAlexander Bulekov    with open(filename, "r") as f:
817c9b64adSAlexander Bulekov        trace = f.readlines()
827c9b64adSAlexander Bulekov
837c9b64adSAlexander Bulekov    # Leave only lines that look like logged qtest commands
847c9b64adSAlexander Bulekov    trace[:] = [x.strip() for x in trace if "[R +" in x
857c9b64adSAlexander Bulekov                or "[S +" in x and "CLOSED" not in x]
867c9b64adSAlexander Bulekov
877c9b64adSAlexander Bulekov    for i in range(len(trace)):
887c9b64adSAlexander Bulekov        if i+1 < len(trace):
897c9b64adSAlexander Bulekov            if "[DMA]" in trace[i+1]:
907c9b64adSAlexander Bulekov                if "[DOUBLE-FETCH]" in trace[i+1]:
917c9b64adSAlexander Bulekov                    sys.stderr.write("Warning: Likely double fetch on line"
927c9b64adSAlexander Bulekov                                     "{}.\n There will likely be problems "
937c9b64adSAlexander Bulekov                                     "reproducing behavior with the "
947c9b64adSAlexander Bulekov                                     "resulting qtest trace\n\n".format(i+1))
957c9b64adSAlexander Bulekov                trace[i], trace[i+1] = trace[i+1], trace[i]
967c9b64adSAlexander Bulekov    for line in trace:
977c9b64adSAlexander Bulekov        print(line.split("]")[-1].strip())
987c9b64adSAlexander Bulekov
997c9b64adSAlexander Bulekov
1007c9b64adSAlexander Bulekovif __name__ == '__main__':
1017c9b64adSAlexander Bulekov    if len(sys.argv) == 1:
1027c9b64adSAlexander Bulekov        usage()
1037c9b64adSAlexander Bulekov    main(sys.argv[1])
104