ayrton
880c8e0713
- Counter for generated tests - Nicer messages when things go wrong Took 51 minutes
179 lines
7.2 KiB
Python
179 lines
7.2 KiB
Python
import os
|
|
import random
|
|
import sys
|
|
import warnings
|
|
from contextlib import redirect_stdout
|
|
|
|
import yaml
|
|
|
|
import fuzzer as fz
|
|
import argparse
|
|
|
|
import xml.dom.minidom
|
|
import xml.etree.ElementTree as ET
|
|
|
|
from constants import NoneTagException
|
|
|
|
import signal as sig
|
|
|
|
|
|
def gprint(expr, end=''):
|
|
if type(expr) is bool:
|
|
if expr:
|
|
print('T', end=end)
|
|
else:
|
|
print('F', end=end)
|
|
else:
|
|
print(expr, end=end)
|
|
|
|
|
|
class Fuzzer():
|
|
def __init__(self, config: str, batch: int, seed: str, file_name: str = "fuzz"):
|
|
with open(config) as yaml_file:
|
|
settings: dict = yaml.safe_load(yaml_file)
|
|
self.settings = settings
|
|
self.batch = batch
|
|
random.seed(seed)
|
|
self.file_name = file_name
|
|
|
|
self.fuzzer = fz.GazpreaFuzzer(config)
|
|
|
|
def fuzz(self):
|
|
sig.signal(sig.SIGALRM, handler)
|
|
sig.alarm(30)
|
|
|
|
base_dir = os.getcwd()
|
|
fuzzer_root = base_dir + '/fuzzer'
|
|
|
|
fuzzer_input = fuzzer_root + '/input/fuzzer'
|
|
fuzzer_debug = fuzzer_root + '/debug'
|
|
fuzzer_asterr = fuzzer_debug + '/ast_err'
|
|
fuzzer_instream = fuzzer_root + '/instream'
|
|
fuzzer_outputs = fuzzer_root + '/outputs/fuzzer'
|
|
fuzzer_ground_truth = fuzzer_root + '/ground_truth'
|
|
|
|
os.system(f"rm -rf {fuzzer_root}")
|
|
os.makedirs(fuzzer_root)
|
|
os.makedirs(fuzzer_input)
|
|
os.makedirs(fuzzer_debug)
|
|
os.makedirs(fuzzer_asterr)
|
|
os.makedirs(fuzzer_instream)
|
|
os.makedirs(fuzzer_outputs)
|
|
os.makedirs(fuzzer_ground_truth)
|
|
try:
|
|
os.system(f"cp tester_config.json {fuzzer_root}/tester_config.json")
|
|
except Exception as e:
|
|
pass
|
|
|
|
i = 0
|
|
min_i = 0
|
|
while i < self.batch:
|
|
try:
|
|
self.fuzzer.fuzz()
|
|
except NoneTagException as n:
|
|
r = random.randint(0, 1000000)
|
|
warnings.warn("None Tag Exception encountered, writing stack trace and xml to debug/ast/{}.xml\n"
|
|
"Look for a top-level <argument> tag or send it to Ayrton and I'll see what I can see"
|
|
"".format(r))
|
|
with open(f"{fuzzer_asterr}/{r}.xml", 'w') as f:
|
|
try:
|
|
f.write(xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml())
|
|
except AttributeError:
|
|
print("Failed to write to the file", file=sys.stderr)
|
|
if i - 1 >= min_i:
|
|
i -= 1
|
|
else:
|
|
i = min_i
|
|
continue
|
|
dom = xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8'))
|
|
pretty: str = dom.toprettyxml()
|
|
|
|
with open("{}/{}_{}.py".format(fuzzer_ground_truth, self.file_name, i), 'w') as f:
|
|
f.write(self.fuzzer.ground_truth)
|
|
with open("{}/{}_{}.py".format(fuzzer_ground_truth, self.file_name, i), 'r') as f:
|
|
with open("{}/{}_{}.out".format(fuzzer_outputs, self.file_name, i), 'w') as y:
|
|
with redirect_stdout(y): # Workaround for fuzzer.py:49
|
|
try:
|
|
read = f.read()
|
|
exec(read, globals())
|
|
except (OverflowError, ZeroDivisionError, ValueError, TypeError, SyntaxError):
|
|
os.system("rm -f {}/{}_{}.py".format(fuzzer_ground_truth, self.file_name, i))
|
|
os.system("rm -f {}/{}_{}.out".format(fuzzer_outputs, self.file_name, i))
|
|
warnings.warn("Runtime error encountered, retrying")
|
|
if i - 1 >= min_i:
|
|
i -= 1
|
|
else:
|
|
i = min_i
|
|
continue
|
|
except KeyboardInterrupt:
|
|
r = random.randint(0, 1000000)
|
|
warnings.warn("Execution halted, result written to debug/ast/{}.xml\n"
|
|
"".format(r))
|
|
with open("{}/{}.xml".format(fuzzer_asterr, r), 'w') as f:
|
|
f.write(xml.dom.minidom.parseString(
|
|
ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml())
|
|
sys.exit(1)
|
|
except TimeoutError:
|
|
r = random.randint(0, 1000000)
|
|
warnings.warn("Execution timed out, result written to debug/ast/{}.xml\n"
|
|
"".format(r))
|
|
with open("{}/{}.xml".format(fuzzer_asterr, r), 'w') as f:
|
|
f.write(xml.dom.minidom.parseString(
|
|
ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml())
|
|
if i - 1 >= min_i:
|
|
i -= 1
|
|
else:
|
|
i = min_i
|
|
continue
|
|
|
|
with open("{}/{}_{}.out".format(fuzzer_outputs, self.file_name, i), 'r') as y:
|
|
out = y.read()
|
|
if out == "":
|
|
print("Empty output, skipping", file=sys.stderr)
|
|
os.system("rm -f {}/{}_{}.py".format(fuzzer_ground_truth, self.file_name, i))
|
|
os.system("rm -f {}/{}_{}.out".format(fuzzer_outputs, self.file_name, i))
|
|
if i - 1 >= min_i:
|
|
i -= 1
|
|
else:
|
|
i = min_i
|
|
continue
|
|
|
|
|
|
with open("{}/{}_{}.in".format(fuzzer_input, self.file_name, i), 'w') as f:
|
|
f.write(self.fuzzer.source)
|
|
with open("{}/{}_{}.xml".format(fuzzer_debug, self.file_name, i), 'w') as f:
|
|
f.write(pretty)
|
|
# y.write(self.fuzzer.out)
|
|
# with open("fuzzer/instream/{}.in".format(i), 'w') as f:
|
|
# f.write(self.fuzzer.source)
|
|
# with open("fuzzer/outputs/{}.out".format(i), 'w') as f:
|
|
# f.write(self.fuzzer.out)
|
|
print("test {}/{} generated".format(i, self.batch))
|
|
i += 1
|
|
min_i = i
|
|
|
|
|
|
def handler(signum, frame):
|
|
print("Execution Timeout")
|
|
raise TimeoutError
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='Procedurally generate a test case for Gazprea'
|
|
)
|
|
parser.add_argument('-b', '--batch_size', type=int, required=False, default=1,
|
|
help="generate BATCH cases (fuzzer/source/nameX.in, /instream/..., /outputs/...)")
|
|
parser.add_argument('--seed', type=int, required=False, action="store",
|
|
help="rng seed")
|
|
parser.add_argument('config_file', type=str, action="store",
|
|
help="path to your configuration file")
|
|
parser.add_argument('file_name', type=str, action="store",
|
|
help="name for the generated files")
|
|
|
|
args = parser.parse_args()
|
|
fuzzer = Fuzzer(config=args.config_file, batch=args.batch_size, seed=args.seed)
|
|
fuzzer.fuzz()
|