5b1aa8be |
'''
This script is a convenience tool to run a standalone fuzz target against each
item in its associated fuzz corpus.
'''
from __future__ import print_function, division, absolute_import
import argparse
import os
import subprocess
import sys
import tempfile
import threading
def which(program):
'''
Implements bash "which" feature.
Find the full path to a program located in the PATH.
https://stackoverflow.com/a/377028
'''
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, _ = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
def cmd(command):
'''
Run a command in a subprocess.
https://stackoverflow.com/a/4408409
https://stackoverflow.com/a/10012262
'''
with tempfile.TemporaryFile() as tempf:
p = subprocess.Popen(command, stderr=tempf)
is_killed = {'value': False}
def timeout(p, is_killed):
is_killed['value'] = True
p.kill()
timer = threading.Timer(2, timeout, [p, is_killed])
try:
timer.start()
p.wait()
tempf.seek(0)
text = tempf.read().decode("utf-8").strip()
returncode = p.returncode
finally:
timer.cancel()
if is_killed['value']:
text = 'error: timeout, ' + text
returncode = 1
return text, returncode
def run_test(fuzzer, corpus_path):
'''
Test a standalone fuzz target with each item from the fuzz corpus.
'''
builddir = os.environ.get("builddir", ".")
fuzz_target = os.path.join(builddir, fuzzer)
print("Fuzz Target: {fuzzer}".format(fuzzer=fuzzer))
print("Corpus Path: {corpus_path}".format(corpus_path=corpus_path))
if not os.path.exists(fuzz_target):
print("Failed to find fuzz target: {binary}!".format(binary=fuzz_target))
sys.exit(1)
failures = 0
valgrind = None
if os.environ.get('VG', ''):
valgrind = which('valgrind')
for fname in os.listdir(corpus_path):
seedpath = os.path.join(corpus_path, fname)
text, returncode = cmd([fuzz_target, seedpath])
if text.strip():
print(text)
failed = False
if returncode != 0 or 'error' in text:
print('failure on %s' % fname)
failed = True
if valgrind:
text, returncode = cmd(
[valgrind, '--error-exitcode=1', fuzz_target, seedpath])
if returncode:
print(text)
print('failure on %s' % fname)
failed = True
if failed:
failures = failures + 1
if failures:
print("%i scanfile fuzzer related tests failed." % failures)
sys.exit(1)
def main():
'''
Get command line options to support this tool.
'''
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'-f',
'--fuzzer',
required=True,
help="The fuzz target to test.")
parser.add_argument(
'-c',
'--corpus',
required=True,
help="Path of the fuzz corpus.")
args = parser.parse_args()
run_test(args.fuzzer, args.corpus)
if __name__ == '__main__':
main() |