2018-12-04 14:26:42 -08:00
|
|
|
#!/usr/bin/env python
|
2024-01-12 17:03:59 -05:00
|
|
|
# Copyright (C) 2018-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
2018-12-04 14:26:42 -08:00
|
|
|
|
|
|
|
'''
|
|
|
|
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()
|