Ejemplo n.º 1
0
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()
Ejemplo n.º 2
0
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
Ejemplo n.º 3
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
Ejemplo n.º 4
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)
Ejemplo n.º 5
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)
Ejemplo n.º 6
0
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()