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 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): 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 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: 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 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 {}/{}_{}.py".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) 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) i += 1 min_i = i 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()