def scan_crashes(base_dir, cmdline_path=None, env_path=None, tool_name=None, test_path=None, firefox=None, firefox_prefs=None, firefox_extensions=None, firefox_testpath=None): ''' Scan the base directory for crash tests and submit them to FuzzManager. @type base_dir: String @param base_dir: AFL base directory @type cmdline_path: String @param cmdline_path: Optional command line file to use instead of the one found inside the base directory. @type env_path: String @param env_path: Optional file containing environment variables. @type test_path: String @param test_path: Optional filename where to copy the test before attempting to reproduce a crash. @rtype: int @return: Non-zero return code on failure ''' crash_dir = os.path.join(base_dir, "crashes") crash_files = [] for crash_file in os.listdir(crash_dir): # Ignore all files that aren't crash results if not crash_file.startswith("id:"): continue crash_file = os.path.join(crash_dir, crash_file) # Ignore our own status files if crash_file.endswith(".submitted") or crash_file.endswith(".failed"): continue # Ignore files we already processed if os.path.exists(crash_file + ".submitted") or os.path.exists(crash_file + ".failed"): continue crash_files.append(crash_file) if crash_files: # First try to read necessary information for reproducing crashes base_env = {} test_in_env = None if env_path: with open(env_path, 'r') as env_file: for line in env_file: (name,val) = line.rstrip('\n').split("=", 1) base_env[name] = val if '@@' in val: test_in_env = name if not cmdline_path: cmdline_path = os.path.join(base_dir, "cmdline") test_idx, cmdline = command_file_to_list(cmdline_path) if test_idx is not None: orig_test_arg = cmdline[test_idx] configuration = ProgramConfiguration.fromBinary(cmdline[0]) if not configuration: print("Error: Creating program configuration from binary failed. Check your binary configuration file.", file=sys.stderr) return 2 collector = Collector(tool=tool_name) if firefox: (ffpInst, ffCmd, ffEnv) = setup_firefox(cmdline[0], firefox_prefs, firefox_extensions, firefox_testpath) cmdline = ffCmd base_env.update(ffEnv) for crash_file in crash_files: stdin = None env = None if base_env: env = dict(base_env) if test_idx is not None: cmdline[test_idx] = orig_test_arg.replace('@@', crash_file) elif test_in_env is not None: env[test_in_env] = env[test_in_env].replace('@@', crash_file) elif test_path is not None: shutil.copy(crash_file, test_path) else: with open(crash_file, 'r') as crash_fd: stdin = crash_fd.read() print("Processing crash file %s" % crash_file, file=sys.stderr) runner = AutoRunner.fromBinaryArgs(cmdline[0], cmdline[1:], env=env, stdin=stdin) if runner.run(): crash_info = runner.getCrashInfo(configuration) collector.submit(crash_info, crash_file) open(crash_file + ".submitted", 'a').close() print("Success: Submitted crash to server.", file=sys.stderr) else: open(crash_file + ".failed", 'a').close() print("Error: Failed to reproduce the given crash, cannot submit.", file=sys.stderr) if firefox: ffpInst.clean_up()
def main(args=None): '''Command line options.''' # setup argparser parser = argparse.ArgumentParser() parser.add_argument('--version', action='version', version='%s v%s (%s)' % (__file__, __version__, __updated__)) # Crash information parser.add_argument("--stdout", help="File containing STDOUT data", metavar="FILE") parser.add_argument("--stderr", help="File containing STDERR data", metavar="FILE") parser.add_argument("--crashdata", help="File containing external crash data", metavar="FILE") # Actions action_group = parser.add_argument_group( "Actions", "A single action must be selected.") actions = action_group.add_mutually_exclusive_group(required=True) actions.add_argument("--refresh", action='store_true', help="Perform a signature refresh") actions.add_argument("--submit", action='store_true', help="Submit a signature to the server") actions.add_argument("--search", action='store_true', help="Search cached signatures for the given crash") actions.add_argument( "--generate", action='store_true', help="Create a (temporary) local signature in the cache directory") actions.add_argument( "--autosubmit", action='store_true', help= ("Go into auto-submit mode. In this mode, all remaining arguments are interpreted " "as the crashing command. This tool will automatically obtain GDB crash information " "and submit it.")) actions.add_argument( "--download", type=int, help="Download the testcase for the specified crash entry", metavar="ID") actions.add_argument( "--download-all", type=int, help="Download all testcases for the specified signature entry", metavar="ID") actions.add_argument( "--get-clientid", action='store_true', help="Print the client ID used when submitting issues") # Settings parser.add_argument("--sigdir", help="Signature cache directory", metavar="DIR") parser.add_argument("--serverhost", help="Server hostname for remote signature management", metavar="HOST") parser.add_argument("--serverport", type=int, help="Server port to use", metavar="PORT") parser.add_argument("--serverproto", help="Server protocol to use (default is https)", metavar="PROTO") parser.add_argument("--serverauthtokenfile", help="File containing the server authentication token", metavar="FILE") parser.add_argument("--clientid", help="Client ID to use when submitting issues", metavar="ID") parser.add_argument("--platform", help="Platform this crash appeared on", metavar="(x86|x86-64|arm)") parser.add_argument("--product", help="Product this crash appeared on", metavar="PRODUCT") parser.add_argument("--productversion", dest="product_version", help="Product version this crash appeared on", metavar="VERSION") parser.add_argument("--os", help="OS this crash appeared on", metavar="(windows|linux|macosx|b2g|android)") parser.add_argument("--tool", help="Name of the tool that found this issue", metavar="NAME") parser.add_argument( '--args', nargs='+', type=str, help= "List of program arguments. Backslashes can be used for escaping and are stripped." ) parser.add_argument( '--env', nargs='+', type=str, help="List of environment variables in the form 'KEY=VALUE'") parser.add_argument( '--metadata', nargs='+', type=str, help="List of metadata variables in the form 'KEY=VALUE'") parser.add_argument( "--binary", help="Binary that has a configuration file for reading", metavar="BINARY") parser.add_argument("--testcase", help="File containing testcase", metavar="FILE") parser.add_argument( "--testcasequality", default=0, type=int, help= "Integer indicating test case quality (%(default)s is best and default)", metavar="VAL") parser.add_argument( "--testcasesize", type=int, help= "Integer indicating test case size (default is size of testcase data)", metavar="SIZE") # Options that affect how signatures are generated parser.add_argument( "--forcecrashaddr", action='store_true', help="Force including the crash address into the signature") parser.add_argument( "--forcecrashinst", action='store_true', help= "Force including the crash instruction into the signature (GDB only)") parser.add_argument( "--numframes", default=8, type=int, help= "How many frames to include into the signature (default: %(default)s)") parser.add_argument('rargs', nargs=argparse.REMAINDER) # process options opts = parser.parse_args(args=args) # In autosubmit mode, we try to open a configuration file for the binary specified # on the command line. It should contain the binary-specific settings for submitting. if opts.autosubmit: if not opts.rargs: parser.error( "Action --autosubmit requires test arguments to be specified") # Store the binary candidate only if --binary wasn't also specified if not opts.binary: opts.binary = opts.rargs[0] # We also need to check that (apart from the binary), there is only one file on the command line # (the testcase), if it hasn't been explicitely specified. testcase = opts.testcase testcaseidx = None if testcase is None: for idx, arg in enumerate(opts.rargs[1:]): if os.path.exists(arg): if testcase: parser.error( "Multiple potential testcases specified on command line. " "Must explicitly specify test using --testcase.") testcase = arg testcaseidx = idx # Either --autosubmit was specified, or someone specified --binary manually # Check that the binary actually exists if opts.binary and not os.path.exists(opts.binary): parser.error("Error: Specified binary does not exist: %s" % opts.binary) stdout = None stderr = None crashdata = None crashInfo = None args = None env = None metadata = {} if opts.search or opts.generate or opts.submit or opts.autosubmit: if opts.metadata: metadata.update(dict(kv.split('=', 1) for kv in opts.metadata)) if opts.autosubmit: # Try to automatically get arguments from the command line # If the testcase is not the last argument, leave it in the # command line arguments and replace it with a generic placeholder. if testcaseidx == len(opts.rargs[1:]) - 1: args = opts.rargs[1:-1] else: args = opts.rargs[1:] if testcaseidx is not None: args[testcaseidx] = "TESTFILE" else: if opts.args: args = [arg.replace('\\', '') for arg in opts.args] if opts.env: env = dict(kv.split('=', 1) for kv in opts.env) # Start without any ProgramConfiguration configuration = None # If we have a binary, try using that to create our ProgramConfiguration if opts.binary: configuration = ProgramConfiguration.fromBinary(opts.binary) if configuration: if env: configuration.addEnvironmentVariables(env) if args: configuration.addProgramArguments(args) if metadata: configuration.addMetadata(metadata) # If configuring through binary failed, try to manually create ProgramConfiguration from command line arguments if configuration is None: if opts.platform is None or opts.product is None or opts.os is None: parser.error( "Must specify/configure at least --platform, --product and --os" ) configuration = ProgramConfiguration(opts.product, opts.platform, opts.os, opts.product_version, env, args, metadata) if not opts.autosubmit: if opts.stderr is None and opts.crashdata is None: parser.error( "Must specify at least either --stderr or --crashdata file" ) if opts.stdout: with open(opts.stdout) as f: stdout = f.read() if opts.stderr: with open(opts.stderr) as f: stderr = f.read() if opts.crashdata: with open(opts.crashdata) as f: crashdata = f.read() crashInfo = CrashInfo.fromRawCrashData(stdout, stderr, configuration, auxCrashData=crashdata) if opts.testcase: (testCaseData, isBinary) = Collector.read_testcase(opts.testcase) if not isBinary: crashInfo.testcase = testCaseData serverauthtoken = None if opts.serverauthtokenfile: with open(opts.serverauthtokenfile) as f: serverauthtoken = f.read().rstrip() collector = Collector(opts.sigdir, opts.serverhost, opts.serverport, opts.serverproto, serverauthtoken, opts.clientid, opts.tool) if opts.refresh: collector.refresh() return 0 if opts.submit: testcase = opts.testcase collector.submit(crashInfo, testcase, opts.testcasequality, opts.testcasesize, metadata) return 0 if opts.search: (sig, metadata) = collector.search(crashInfo) if sig is None: print("No match found", file=sys.stderr) return 3 print(sig) if metadata: print(json.dumps(metadata, indent=4)) return 0 if opts.generate: sigFile = collector.generate(crashInfo, opts.forcecrashaddr, opts.forcecrashinst, opts.numframes) if not sigFile: print( "Failed to generate a signature for the given crash information.", file=sys.stderr) return 1 print(sigFile) return 0 if opts.autosubmit: runner = AutoRunner.fromBinaryArgs(opts.rargs[0], opts.rargs[1:]) if runner.run(): crashInfo = runner.getCrashInfo(configuration) collector.submit(crashInfo, testcase, opts.testcasequality, opts.testcasesize, metadata) else: print("Error: Failed to reproduce the given crash, cannot submit.", file=sys.stderr) return 1 if opts.download: (retFile, retJSON) = collector.download(opts.download) if not retFile: print("Specified crash entry does not have a testcase", file=sys.stderr) return 1 if "args" in retJSON and retJSON["args"]: args = json.loads(retJSON["args"]) print("Command line arguments: %s" % " ".join(args)) print("") if "env" in retJSON and retJSON["env"]: env = json.loads(retJSON["env"]) print("Environment variables: %s", " ".join("%s = %s" % (k, v) for (k, v) in env.items())) print("") if "metadata" in retJSON and retJSON["metadata"]: metadata = json.loads(retJSON["metadata"]) print("== Metadata ==") for k, v in metadata.items(): print("%s = %s" % (k, v)) print("") print(retFile) return 0 if opts.download_all: downloaded = False for result in collector.download_all(opts.download_all): downloaded = True print(result) if not downloaded: print("Specified signature does not have any testcases", file=sys.stderr) return 1 return 0 if opts.get_clientid: print(collector.clientId) return 0
def main(argv=None): '''Command line options.''' program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = "%s" % __updated__ program_version_string = '%%prog %s (%s)' % (program_version, program_build_date) if argv is None: argv = sys.argv[1:] # setup argparser parser = argparse.ArgumentParser() parser.add_argument('--version', action='version', version=program_version_string) # Crash information parser.add_argument("--stdout", dest="stdout", help="File containing STDOUT data", metavar="FILE") parser.add_argument("--stderr", dest="stderr", help="File containing STDERR data", metavar="FILE") parser.add_argument("--crashdata", dest="crashdata", help="File containing external crash data", metavar="FILE") # Actions parser.add_argument("--refresh", dest="refresh", action='store_true', help="Perform a signature refresh") parser.add_argument("--submit", dest="submit", action='store_true', help="Submit a signature to the server") parser.add_argument("--search", dest="search", action='store_true', help="Search cached signatures for the given crash") parser.add_argument("--generate", dest="generate", action='store_true', help="Create a (temporary) local signature in the cache directory") parser.add_argument("--autosubmit", dest="autosubmit", action='store_true', help="Go into auto-submit mode. In this mode, all remaining arguments are interpreted as the crashing command. This tool will automatically obtain GDB crash information and submit it.") parser.add_argument("--download", dest="download", type=int, help="Download the testcase for the specified crash entry", metavar="ID") # Settings parser.add_argument("--sigdir", dest="sigdir", help="Signature cache directory", metavar="DIR") parser.add_argument("--serverhost", dest="serverhost", help="Server hostname for remote signature management", metavar="HOST") parser.add_argument("--serverport", dest="serverport", type=int, help="Server port to use", metavar="PORT") parser.add_argument("--serverproto", dest="serverproto", help="Server protocol to use (default is https)", metavar="PROTO") parser.add_argument("--serverauthtokenfile", dest="serverauthtokenfile", help="File containing the server authentication token", metavar="FILE") parser.add_argument("--clientid", dest="clientid", help="Client ID to use when submitting issues", metavar="ID") parser.add_argument("--platform", dest="platform", help="Platform this crash appeared on", metavar="(x86|x86-64|arm)") parser.add_argument("--product", dest="product", help="Product this crash appeared on", metavar="PRODUCT") parser.add_argument("--productversion", dest="product_version", help="Product version this crash appeared on", metavar="VERSION") parser.add_argument("--os", dest="os", help="OS this crash appeared on", metavar="(windows|linux|macosx|b2g|android)") parser.add_argument("--tool", dest="tool", help="Name of the tool that found this issue", metavar="NAME") parser.add_argument('--args', dest='args', nargs='+', type=str, help="List of program arguments. Backslashes can be used for escaping and are stripped.") parser.add_argument('--env', dest='env', nargs='+', type=str, help="List of environment variables in the form 'KEY=VALUE'") parser.add_argument('--metadata', dest='metadata', nargs='+', type=str, help="List of metadata variables in the form 'KEY=VALUE'") parser.add_argument("--binary", dest="binary", help="Binary that has a configuration file for reading", metavar="BINARY") parser.add_argument("--testcase", dest="testcase", help="File containing testcase", metavar="FILE") parser.add_argument("--testcasequality", dest="testcasequality", default="0", help="Integer indicating test case quality (0 is best and default)", metavar="VAL") # Options that affect how signatures are generated parser.add_argument("--forcecrashaddr", dest="forcecrashaddr", action='store_true', help="Force including the crash address into the signature") parser.add_argument("--forcecrashinst", dest="forcecrashinst", action='store_true', help="Force including the crash instruction into the signature (GDB only)") parser.add_argument("--numframes", dest="numframes", default=8, type=int, help="How many frames to include into the signature (default is 8)") parser.add_argument('rargs', nargs=argparse.REMAINDER) if len(argv) == 0: parser.print_help() return 2 # process options opts = parser.parse_args(argv) # Check that one action is specified actions = [ "refresh", "submit", "search", "generate", "autosubmit", "download" ] haveAction = False for action in actions: if getattr(opts, action): if haveAction: print("Error: Cannot specify multiple actions at the same time", file=sys.stderr) return 2 haveAction = True if not haveAction: print("Error: Must specify an action", file=sys.stderr) return 2 # In autosubmit mode, we try to open a configuration file for the binary specified # on the command line. It should contain the binary-specific settings for submitting. if opts.autosubmit: if not opts.rargs: print("Error: Action --autosubmit requires test arguments to be specified", file=sys.stderr) return 2 # Store the binary candidate only if --binary wasn't also specified if not opts.binary: opts.binary = opts.rargs[0] # We also need to check that (apart from the binary), there is only one file on the command line # (the testcase), if it hasn't been explicitely specified. testcase = opts.testcase testcaseidx = None if testcase == None: for idx, arg in enumerate(opts.rargs[1:]): if os.path.exists(arg): if testcase: print("Error: Multiple potential testcases specified on command line. Must explicitely specify test using --testcase.") return 2 testcase = arg testcaseidx = idx # Either --autosubmit was specified, or someone specified --binary manually # Check that the binary actually exists if opts.binary and not os.path.exists(opts.binary): print("Error: Specified binary does not exist: %s" % opts.binary) return 2 stdout = None stderr = None crashdata = None crashInfo = None args = None env = None metadata = {} if opts.search or opts.generate or opts.submit or opts.autosubmit: if opts.metadata: metadata.update(dict(kv.split('=', 1) for kv in opts.metadata)) if opts.autosubmit: # Try to automatically get arguments from the command line # If the testcase is not the last argument, leave it in the # command line arguments and replace it with a generic placeholder. if testcaseidx == len(opts.rargs[1:]) - 1: args = opts.rargs[1:-1] else: args = opts.rargs[1:] if testcaseidx != None: args[testcaseidx] = "TESTFILE" else: if opts.args: args = [arg.replace('\\', '') for arg in opts.args] if opts.env: env = dict(kv.split('=', 1) for kv in opts.env) # Start without any ProgramConfiguration configuration = None # If we have a binary, try using that to create our ProgramConfiguration if opts.binary: configuration = ProgramConfiguration.fromBinary(opts.binary) if configuration: if env: configuration.addEnvironmentVariables(env) if args: configuration.addProgramArguments(args) if metadata: configuration.addMetadata(metadata) # If configuring through binary failed, try to manually create ProgramConfiguration from command line arguments if configuration == None: if opts.platform == None or opts.product == None or opts.os == None: print("Error: Must specify/configure at least --platform, --product and --os", file=sys.stderr) return 2 configuration = ProgramConfiguration(opts.product, opts.platform, opts.os, opts.product_version, env, args, metadata) if not opts.autosubmit: if opts.stderr == None and opts.crashdata == None: print("Error: Must specify at least either --stderr or --crashdata file", file=sys.stderr) return 2 if opts.stdout: with open(opts.stdout) as f: stdout = f.read() if opts.stderr: with open(opts.stderr) as f: stderr = f.read() if opts.crashdata: with open(opts.crashdata) as f: crashdata = f.read() crashInfo = CrashInfo.fromRawCrashData(stdout, stderr, configuration, auxCrashData=crashdata) if opts.testcase: (testCaseData, isBinary) = Collector.read_testcase(opts.testcase) if not isBinary: crashInfo.testcase = testCaseData serverauthtoken = None if opts.serverauthtokenfile: with open(opts.serverauthtokenfile) as f: serverauthtoken = f.read().rstrip() collector = Collector(opts.sigdir, opts.serverhost, opts.serverport, opts.serverproto, serverauthtoken, opts.clientid, opts.tool) if opts.refresh: collector.refresh() return 0 if opts.submit: testcase = opts.testcase collector.submit(crashInfo, testcase, opts.testcasequality, metadata) return 0 if opts.search: (sig, metadata) = collector.search(crashInfo) if sig == None: print("No match found") return 3 print(sig) if metadata: print(json.dumps(metadata, indent=4)) return 0 if opts.generate: sigFile = collector.generate(crashInfo, opts.forcecrashaddr, opts.forcecrashinst, opts.numframes) if not sigFile: print("Failed to generate a signature for the given crash information.", file=sys.stderr) return 2 print(sigFile) return 0 if opts.autosubmit: runner = AutoRunner.fromBinaryArgs(opts.rargs[0], opts.rargs[1:]) if runner.run(): crashInfo = runner.getCrashInfo(configuration) collector.submit(crashInfo, testcase, opts.testcasequality, metadata) else: print("Error: Failed to reproduce the given crash, cannot submit.", file=sys.stderr) return 2 if opts.download: (retFile, retJSON) = collector.download(opts.download) if not retFile: print("Specified crash entry does not have a testcase", file=sys.stderr) return 2 if "args" in retJSON and retJSON["args"]: args = json.loads(retJSON["args"]) print("Command line arguments: %s" % " ".join(args)) print("") if "env" in retJSON and retJSON["env"]: env = json.loads(retJSON["env"]) print("Environment variables: %s", " ".join([ "%s = %s" % (k,v) for (k,v) in env.items()])) print("") if "metadata" in retJSON and retJSON["metadata"]: metadata = json.loads(retJSON["metadata"]) print("== Metadata ==") for k, v in metadata.items(): print("%s = %s" % (k,v)) print("") print(retFile) return 0
def scan_crashes(base_dir, cmdline_path=None): ''' Scan the base directory for crash tests and submit them to FuzzManager. @type base_dir: String @param base_dir: AFL base directory @type cmdline_path: String @param cmdline_path: Optional command line file to use instead of the one found inside the base directory. @rtype: int @return: Non-zero return code on failure ''' crash_dir = os.path.join(base_dir, "crashes") crash_files = [] for crash_file in os.listdir(crash_dir): # Ignore all files that aren't crash results if not crash_file.startswith("id:"): continue crash_file = os.path.join(crash_dir, crash_file) # Ignore our own status files if crash_file.endswith(".submitted") or crash_file.endswith(".failed"): continue # Ignore files we already processed if os.path.exists(crash_file + ".submitted") or os.path.exists(crash_file + ".failed"): continue crash_files.append(crash_file) if crash_files: # First try to read necessary information for reproducing crashes cmdline = [] test_idx = None if not cmdline_path: cmdline_path = os.path.join(base_dir, "cmdline") with open(cmdline_path, 'r') as cmdline_file: idx = 0 for line in cmdline_file: if '@@' in line: test_idx = idx cmdline.append(line.rstrip('\n')) idx += 1 if test_idx != None: orig_test_arg = cmdline[test_idx] print(cmdline) configuration = ProgramConfiguration.fromBinary(cmdline[0]) if not configuration: print( "Error: Creating program configuration from binary failed. Check your binary configuration file.", file=sys.stderr) return 2 collector = Collector() for crash_file in crash_files: stdin = None if test_idx != None: cmdline[test_idx] = orig_test_arg.replace('@@', crash_file) else: with open(crash_file, 'r') as crash_fd: stdin = crash_fd.read() runner = AutoRunner.fromBinaryArgs(cmdline[0], cmdline[1:], stdin=stdin) if runner.run(): crash_info = runner.getCrashInfo(configuration) collector.submit(crash_info, crash_file) open(crash_file + ".submitted", 'a').close() else: open(crash_file + ".failed", 'a').close() print( "Error: Failed to reproduce the given crash, cannot submit.", file=sys.stderr)
def scan_crashes(base_dir, cmdline_path=None): ''' Scan the base directory for crash tests and submit them to FuzzManager. @type base_dir: String @param base_dir: AFL base directory @type cmdline_path: String @param cmdline_path: Optional command line file to use instead of the one found inside the base directory. @rtype: int @return: Non-zero return code on failure ''' crash_dir = os.path.join(base_dir, "crashes") crash_files = [] for crash_file in os.listdir(crash_dir): # Ignore all files that aren't crash results if not crash_file.startswith("id:"): continue crash_file = os.path.join(crash_dir, crash_file) # Ignore our own status files if crash_file.endswith(".submitted") or crash_file.endswith(".failed"): continue # Ignore files we already processed if os.path.exists(crash_file + ".submitted") or os.path.exists(crash_file + ".failed"): continue crash_files.append(crash_file) if crash_files: # First try to read necessary information for reproducing crashes cmdline = [] test_idx = None if not cmdline_path: cmdline_path = os.path.join(base_dir, "cmdline") with open(cmdline_path, 'r') as cmdline_file: idx = 0 for line in cmdline_file: if '@@' in line: test_idx = idx cmdline.append(line.rstrip('\n')) idx += 1 if test_idx != None: orig_test_arg = cmdline[test_idx] print(cmdline) configuration = ProgramConfiguration.fromBinary(cmdline[0]) if not configuration: print("Error: Creating program configuration from binary failed. Check your binary configuration file.", file=sys.stderr) return 2 collector = Collector() for crash_file in crash_files: stdin = None if test_idx != None: cmdline[test_idx] = orig_test_arg.replace('@@', crash_file) else: with open(crash_file, 'r') as crash_fd: stdin = crash_fd.read() runner = AutoRunner.fromBinaryArgs(cmdline[0], cmdline[1:], stdin=stdin) if runner.run(): crash_info = runner.getCrashInfo(configuration) collector.submit(crash_info, crash_file) open(crash_file + ".submitted", 'a').close() else: open(crash_file + ".failed", 'a').close() print("Error: Failed to reproduce the given crash, cannot submit.", file=sys.stderr)
def scan_crashes(base_dir, cmdline_path=None, env_path=None, tool_name=None, firefox=None, firefox_prefs=None, firefox_extensions=None, firefox_testpath=None): ''' Scan the base directory for crash tests and submit them to FuzzManager. @type base_dir: String @param base_dir: AFL base directory @type cmdline_path: String @param cmdline_path: Optional command line file to use instead of the one found inside the base directory. @type env_path: String @param env_path: Optional file containing environment variables. @type test_path: String @param test_path: Optional filename where to copy the test before attempting to reproduce a crash. @rtype: int @return: Non-zero return code on failure ''' crash_dir = os.path.join(base_dir, "crashes") crash_files = [] for crash_file in os.listdir(crash_dir): # Ignore all files that aren't crash results if not crash_file.startswith("id:"): continue crash_file = os.path.join(crash_dir, crash_file) # Ignore our own status files if crash_file.endswith(".submitted") or crash_file.endswith(".failed"): continue # Ignore files we already processed if os.path.exists(crash_file + ".submitted") or os.path.exists(crash_file + ".failed"): continue crash_files.append(crash_file) if crash_files: # First try to read necessary information for reproducing crashes cmdline = [] test_idx = None base_env = {} test_in_env = None if env_path: with open(env_path, 'r') as env_file: for line in env_file: (name, val) = line.rstrip('\n').split("=", 1) base_env[name] = val if '@@' in val: test_in_env = name if not cmdline_path: cmdline_path = os.path.join(base_dir, "cmdline") with open(cmdline_path, 'r') as cmdline_file: idx = 0 for line in cmdline_file: if '@@' in line: test_idx = idx cmdline.append(line.rstrip('\n')) idx += 1 if test_idx != None: orig_test_arg = cmdline[test_idx] configuration = ProgramConfiguration.fromBinary(cmdline[0]) if not configuration: print( "Error: Creating program configuration from binary failed. Check your binary configuration file.", file=sys.stderr) return 2 collector = Collector(tool=tool_name) if firefox: (ffpInst, ffCmd, ffEnv) = setup_firefox(cmdline[0], firefox_prefs, firefox_extensions, firefox_testpath) cmdline = ffCmd base_env.update(ffEnv) for crash_file in crash_files: stdin = None env = None if base_env: env = dict(base_env) if test_idx != None: cmdline[test_idx] = orig_test_arg.replace('@@', crash_file) elif test_in_env != None: env[test_in_env] = env[test_in_env].replace('@@', crash_file) elif test_path != None: shutil.copy(crash_file, test_path) else: with open(crash_file, 'r') as crash_fd: stdin = crash_fd.read() print("Processing crash file %s" % crash_file, file=sys.stderr) runner = AutoRunner.fromBinaryArgs(cmdline[0], cmdline[1:], env=env, stdin=stdin) if runner.run(): crash_info = runner.getCrashInfo(configuration) collector.submit(crash_info, crash_file) open(crash_file + ".submitted", 'a').close() print("Success: Submitted crash to server.", file=sys.stderr) else: open(crash_file + ".failed", 'a').close() print( "Error: Failed to reproduce the given crash, cannot submit.", file=sys.stderr) if firefox: ffpInst.clean_up()