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