Esempio n. 1
0
    def libfuzzer(
        self,
        project: str,
        name: str,
        build: str,
        pool_name: PoolName,
        *,
        reports: Optional[List[str]] = None,
        crashes: Optional[List[File]] = None,
        target_exe: File = File("fuzz.exe"),
        tags: Optional[Dict[str, str]] = None,
        notification_config: Optional[NotificationConfig] = None,
        target_env: Optional[Dict[str, str]] = None,
        setup_dir: Optional[Directory] = None,
        reboot_after_setup: bool = False,
        target_options: Optional[List[str]] = None,
        dryrun: bool = False,
        duration: int = 24,
        crash_report_timeout: Optional[int] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        check_retry_count: Optional[int] = None,
        check_fuzzer_help: bool = True,
        delete_input_container: bool = True,
        check_regressions: bool = False,
    ) -> None:
        """
        libfuzzer regression task

        :param File crashes: Specify crashing input files to check in the regression task
        :param str reports: Specify specific report names to verify in the regression task
        :param bool check_regressions: Specify if exceptions should be thrown on finding crash regressions
        :param bool delete_input_container: Specify wether or not to delete the input container
        """

        self._create_job(
            TaskType.libfuzzer_regression,
            project,
            name,
            build,
            pool_name,
            crashes=crashes,
            reports=reports,
            target_exe=target_exe,
            tags=tags,
            notification_config=notification_config,
            target_env=target_env,
            setup_dir=setup_dir,
            reboot_after_setup=reboot_after_setup,
            target_options=target_options,
            dryrun=dryrun,
            duration=duration,
            crash_report_timeout=crash_report_timeout,
            debug=debug,
            check_retry_count=check_retry_count,
            check_fuzzer_help=check_fuzzer_help,
            delete_input_container=delete_input_container,
            check_regressions=check_regressions,
        )
Esempio n. 2
0
    def _run(self, target_os: OS, test_id: UUID, base: Directory,
             target: str) -> None:
        pool = PoolName(f"{target}-{target_os.name}-{test_id}")
        self.onefuzz.pools.create(pool, target_os)
        self.onefuzz.scalesets.create(pool, 5)
        broken = File(os.path.join(base, target, "broken.exe"))
        fixed = File(os.path.join(base, target, "fixed.exe"))

        self.logger.info("starting first build")
        self._run_job(test_id, pool, target, broken, 1)

        self.logger.info("starting second build")
        job = self._run_job(test_id, pool, target, fixed, 2)
        if self._check_regression(job):
            raise Exception("fixed binary should be a no repro")

        self.logger.info("starting third build")
        job = self._run_job(test_id, pool, target, broken, 3)
        if not self._check_regression(job):
            raise Exception("broken binary should be a crash report")

        self.onefuzz.pools.shutdown(pool, now=True)
Esempio n. 3
0
    def _copy_exe(self, src_sas: str, dst_sas: str, target_exe: File) -> None:
        files: List[File] = [target_exe]
        pdb_path = os.path.splitext(target_exe)[0] + ".pdb"
        if os.path.exists(str(pdb_path)):
            files.append(File(pdb_path))

        for path in files:
            filename = os.path.basename(path)
            src_url = container_file_path(src_sas, filename)
            dst_url = container_file_path(dst_sas, filename)

            cmd = [
                "azcopy",
                "copy",
                src_url,
                dst_url,
            ]
            self.logger.info("uploading %s", path)
            subprocess.check_output(cmd)
Esempio n. 4
0
    def _copy_exe(self, src_sas: str, dst_sas: str, target_exe: File) -> None:
        files: List[File] = [target_exe]
        pdb_path = os.path.splitext(target_exe)[0] + ".pdb"
        if os.path.exists(str(pdb_path)):
            files.append(File(pdb_path))

        for path in files:
            filename = os.path.basename(path)
            src_url = container_file_path(src_sas, filename)
            dst_url = container_file_path(dst_sas, filename)

            cmd = [
                "azcopy",
                "copy",
                src_url,
                dst_url,
            ]
            self.logger.info("uploading %s", path)
            # security note: the source and destination container sas URLS
            # are considerd trusted from the service
            subprocess.check_output(cmd)  # nosec
Esempio n. 5
0
    def qemu_user(
        self,
        project: str,
        name: str,
        build: str,
        pool_name: PoolName,
        *,
        arch: QemuArch = QemuArch.aarch64,
        target_exe: File = File("fuzz.exe"),
        sysroot: Optional[File] = None,
        vm_count: int = 1,
        inputs: Optional[Directory] = None,
        reboot_after_setup: bool = False,
        duration: int = 24,
        target_workers: Optional[int] = 1,
        target_options: Optional[List[str]] = None,
        fuzzing_target_options: Optional[List[str]] = None,
        target_env: Optional[Dict[str, str]] = None,
        tags: Optional[Dict[str, str]] = None,
        wait_for_running: bool = False,
        wait_for_files: Optional[List[ContainerType]] = None,
        existing_inputs: Optional[Container] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        ensemble_sync_delay: Optional[int] = None,
        colocate_all_tasks: bool = False,
        crash_report_timeout: Optional[int] = 1,
        check_retry_count: Optional[int] = 300,
        check_fuzzer_help: bool = True,
    ) -> Optional[Job]:
        """
        libfuzzer tasks, wrapped via qemu-user (PREVIEW FEATURE)
        """

        self.logger.warning(
            "qemu_user jobs are a preview feature and may change in the future"
        )

        pool = self.onefuzz.pools.get(pool_name)
        if pool.os != OS.linux:
            raise Exception(
                "libfuzzer qemu-user jobs are only compatible with Linux")

        self._check_is_libfuzzer(target_exe)

        if target_options is None:
            target_options = []

        # disable detect_leaks, as this is non-functional on cross-compile targets
        if target_env is None:
            target_env = {}
        target_env["ASAN_OPTIONS"] = (target_env.get("ASAN_OPTIONS", "") +
                                      ":detect_leaks=0")

        helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            name,
            build,
            duration,
            pool_name=pool_name,
            target_exe=target_exe,
        )

        helper.add_tags(tags)
        helper.define_containers(
            ContainerType.setup,
            ContainerType.inputs,
            ContainerType.crashes,
            ContainerType.reports,
            ContainerType.unique_reports,
            ContainerType.no_repro,
        )

        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)
            helper.containers[ContainerType.inputs] = existing_inputs
        else:
            helper.define_containers(ContainerType.inputs)

        fuzzer_containers = [
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            (ContainerType.inputs, helper.containers[ContainerType.inputs]),
        ]

        helper.create_containers()

        target_exe_blob_name = helper.setup_relative_blob_name(
            target_exe, None)

        wrapper_name = File(target_exe_blob_name + "-wrapper.sh")

        with tempfile.TemporaryDirectory() as tempdir:
            if sysroot:
                setup_path = File(os.path.join(tempdir, "setup.sh"))
                with open(setup_path, "w", newline="\n") as handle:
                    sysroot_filename = helper.setup_relative_blob_name(
                        sysroot, None)
                    handle.write(
                        "#!/bin/bash\n"
                        "set -ex\n"
                        "sudo apt-get install -y qemu-user g++-aarch64-linux-gnu libasan5-arm64-cross\n"
                        'cd $(dirname "$(readlink -f "$0")")\n'
                        "mkdir -p sysroot\n"
                        "tar -C sysroot -zxvf %s\n" % sysroot_filename)

                wrapper_path = File(os.path.join(tempdir, wrapper_name))
                with open(wrapper_path, "w", newline="\n") as handle:
                    handle.write(
                        "#!/bin/bash\n"
                        'SETUP_DIR=$(dirname "$(readlink -f "$0")")\n'
                        "qemu-%s -L $SETUP_DIR/sysroot $SETUP_DIR/%s $*" %
                        (arch.name, target_exe_blob_name))
                upload_files = [setup_path, wrapper_path, sysroot]
            else:
                setup_path = File(os.path.join(tempdir, "setup.sh"))
                with open(setup_path, "w", newline="\n") as handle:
                    handle.write(
                        "#!/bin/bash\n"
                        "set -ex\n"
                        "sudo apt-get install -y qemu-user g++-aarch64-linux-gnu libasan5-arm64-cross\n"
                    )

                wrapper_path = File(os.path.join(tempdir, wrapper_name))
                with open(wrapper_path, "w", newline="\n") as handle:
                    handle.write(
                        "#!/bin/bash\n"
                        'SETUP_DIR=$(dirname "$(readlink -f "$0")")\n'
                        "qemu-%s -L /usr/%s-linux-gnu $SETUP_DIR/%s $*" %
                        (arch.name, arch.name, target_exe_blob_name))
                upload_files = [setup_path, wrapper_path]
            helper.upload_setup(None, target_exe, upload_files)

        if inputs:
            helper.upload_inputs(inputs)
        helper.wait_on(wait_for_files, wait_for_running)

        # Build `target_options` for the `libfuzzer_fuzz` task.
        #
        # This allows passing arguments like `-runs` to the target only when
        # invoked in persistent fuzzing mode, and not test case repro mode.
        libfuzzer_fuzz_target_options = target_options.copy()

        if fuzzing_target_options:
            libfuzzer_fuzz_target_options += fuzzing_target_options

        self.logger.info("creating libfuzzer_fuzz task")
        fuzzer_task = self.onefuzz.tasks.create(
            helper.job.job_id,
            TaskType.libfuzzer_fuzz,
            wrapper_name,
            fuzzer_containers,
            pool_name=pool_name,
            reboot_after_setup=reboot_after_setup,
            duration=duration,
            vm_count=vm_count,
            target_options=libfuzzer_fuzz_target_options,
            target_env=target_env,
            target_workers=target_workers,
            tags=tags,
            debug=debug,
            ensemble_sync_delay=ensemble_sync_delay,
            expect_crash_on_failure=False,
            check_fuzzer_help=check_fuzzer_help,
        )

        report_containers = [
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            (ContainerType.reports, helper.containers[ContainerType.reports]),
            (
                ContainerType.unique_reports,
                helper.containers[ContainerType.unique_reports],
            ),
            (ContainerType.no_repro,
             helper.containers[ContainerType.no_repro]),
        ]

        self.logger.info("creating libfuzzer_crash_report task")
        self.onefuzz.tasks.create(
            helper.job.job_id,
            TaskType.libfuzzer_crash_report,
            wrapper_name,
            report_containers,
            pool_name=pool_name,
            duration=duration,
            vm_count=1,
            reboot_after_setup=reboot_after_setup,
            target_options=target_options,
            target_env=target_env,
            tags=tags,
            prereq_tasks=[fuzzer_task.task_id],
            target_timeout=crash_report_timeout,
            check_retry_count=check_retry_count,
            debug=debug,
            colocate=colocate_all_tasks,
            expect_crash_on_failure=False,
            check_fuzzer_help=check_fuzzer_help,
        )

        self.logger.info("done creating tasks")
        helper.wait()
        return helper.job
Esempio n. 6
0
    def dotnet(
        self,
        project: str,
        name: str,
        build: str,
        pool_name: PoolName,
        *,
        setup_dir: Directory,
        target_harness: str,
        vm_count: int = 1,
        inputs: Optional[Directory] = None,
        reboot_after_setup: bool = False,
        duration: int = 24,
        target_workers: Optional[int] = None,
        target_options: Optional[List[str]] = None,
        fuzzing_target_options: Optional[List[str]] = None,
        target_env: Optional[Dict[str, str]] = None,
        tags: Optional[Dict[str, str]] = None,
        wait_for_running: bool = False,
        wait_for_files: Optional[List[ContainerType]] = None,
        existing_inputs: Optional[Container] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        ensemble_sync_delay: Optional[int] = None,
        check_fuzzer_help: bool = True,
        expect_crash_on_failure: bool = False,
    ) -> Optional[Job]:
        """
        libfuzzer-dotnet task
        """

        harness = "libfuzzer-dotnet"

        pool = self.onefuzz.pools.get(pool_name)
        if pool.os != OS.linux:
            raise Exception(
                "libfuzzer-dotnet jobs are only compatable on linux")

        target_exe = File(os.path.join(setup_dir, harness))
        if not os.path.exists(target_exe):
            raise Exception(f"missing harness: {target_exe}")

        assembly_path = os.path.join(setup_dir, target_harness)
        if not os.path.exists(assembly_path):
            raise Exception(f"missing assembly: {assembly_path}")

        self._check_is_libfuzzer(target_exe)
        if target_options is None:
            target_options = []
        target_options = ["--target_path={setup_dir}/"
                          f"{target_harness}"] + target_options

        helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            name,
            build,
            duration,
            pool_name=pool_name,
            target_exe=target_exe,
        )

        helper.add_tags(tags)
        helper.define_containers(
            ContainerType.setup,
            ContainerType.inputs,
            ContainerType.crashes,
        )

        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)
            helper.containers[ContainerType.inputs] = existing_inputs
        else:
            helper.define_containers(ContainerType.inputs)

        fuzzer_containers = [
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            (ContainerType.inputs, helper.containers[ContainerType.inputs]),
        ]

        helper.create_containers()

        helper.upload_setup(setup_dir, target_exe)
        if inputs:
            helper.upload_inputs(inputs)
        helper.wait_on(wait_for_files, wait_for_running)

        # Build `target_options` for the `libfuzzer_fuzz` task.
        #
        # This allows passing arguments like `-runs` to the target only when
        # invoked in persistent fuzzing mode, and not test case repro mode.
        libfuzzer_fuzz_target_options = target_options.copy()

        if fuzzing_target_options:
            libfuzzer_fuzz_target_options += fuzzing_target_options

        self.onefuzz.tasks.create(
            helper.job.job_id,
            TaskType.libfuzzer_fuzz,
            harness,
            fuzzer_containers,
            pool_name=pool_name,
            reboot_after_setup=reboot_after_setup,
            duration=duration,
            vm_count=vm_count,
            target_options=libfuzzer_fuzz_target_options,
            target_env=target_env,
            target_workers=target_workers,
            tags=tags,
            debug=debug,
            ensemble_sync_delay=ensemble_sync_delay,
            check_fuzzer_help=check_fuzzer_help,
            expect_crash_on_failure=expect_crash_on_failure,
        )

        self.logger.info("done creating tasks")
        helper.wait()
        return helper.job
Esempio n. 7
0
    def merge(
        self,
        project: str,
        name: str,
        build: str,
        pool_name: PoolName,
        *,
        target_exe: File = File("fuzz.exe"),
        setup_dir: Optional[Directory] = None,
        inputs: Optional[Directory] = None,
        output_container: Optional[Container] = None,
        reboot_after_setup: bool = False,
        duration: int = 24,
        target_options: Optional[List[str]] = None,
        target_env: Optional[Dict[str, str]] = None,
        check_retry_count: Optional[int] = None,
        crash_report_timeout: Optional[int] = None,
        tags: Optional[Dict[str, str]] = None,
        wait_for_running: bool = False,
        wait_for_files: Optional[List[ContainerType]] = None,
        extra_files: Optional[List[File]] = None,
        existing_inputs: Optional[List[Container]] = None,
        dryrun: bool = False,
        notification_config: Optional[NotificationConfig] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        preserve_existing_outputs: bool = False,
        check_fuzzer_help: bool = True,
    ) -> Optional[Job]:
        """
        libfuzzer merge task
        """

        # verify containers exist
        if existing_inputs:
            for existing_container in existing_inputs:
                self.onefuzz.containers.get(existing_container)
        elif not inputs:
            self.logger.error(
                "please specify either an input folder or at least one existing inputs container"
            )
            return None

        if dryrun:
            return None

        self.logger.info("creating libfuzzer merge from template")
        self._check_is_libfuzzer(target_exe)

        helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            name,
            build,
            duration,
            pool_name=pool_name,
            target_exe=target_exe,
        )

        helper.add_tags(tags)
        helper.define_containers(ContainerType.setup, )
        if inputs:
            helper.define_containers(ContainerType.inputs)

        if output_container:
            if self.onefuzz.containers.get(output_container):
                helper.define_containers(ContainerType.unique_inputs)

        helper.create_containers()
        helper.setup_notifications(notification_config)

        helper.upload_setup(setup_dir, target_exe, extra_files)
        if inputs:
            helper.upload_inputs(inputs)
        helper.wait_on(wait_for_files, wait_for_running)

        target_exe_blob_name = helper.setup_relative_blob_name(
            target_exe, setup_dir)

        merge_containers = [
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (
                ContainerType.unique_inputs,
                output_container
                or helper.containers[ContainerType.unique_inputs],
            ),
        ]

        if inputs:
            merge_containers.append((ContainerType.inputs,
                                     helper.containers[ContainerType.inputs]))
        if existing_inputs:
            for existing_container in existing_inputs:
                merge_containers.append(
                    (ContainerType.inputs, existing_container))

        self.logger.info("creating libfuzzer_merge task")
        self.onefuzz.tasks.create(
            helper.job.job_id,
            TaskType.libfuzzer_merge,
            target_exe_blob_name,
            merge_containers,
            pool_name=pool_name,
            duration=duration,
            vm_count=1,
            reboot_after_setup=reboot_after_setup,
            target_options=target_options,
            target_env=target_env,
            tags=tags,
            target_timeout=crash_report_timeout,
            check_retry_count=check_retry_count,
            debug=debug,
            preserve_existing_outputs=preserve_existing_outputs,
            check_fuzzer_help=check_fuzzer_help,
        )

        self.logger.info("done creating tasks")
        helper.wait()
        return helper.job
Esempio n. 8
0
    def basic(
        self,
        project: str,
        name: str,
        build: str,
        pool_name: PoolName,
        *,
        target_exe: File = File("fuzz.exe"),
        setup_dir: Optional[Directory] = None,
        vm_count: int = 2,
        inputs: Optional[Directory] = None,
        reboot_after_setup: bool = False,
        duration: int = 24,
        target_workers: Optional[int] = None,
        target_options: Optional[List[str]] = None,
        fuzzing_target_options: Optional[List[str]] = None,
        target_env: Optional[Dict[str, str]] = None,
        target_timeout: Optional[int] = None,
        check_retry_count: Optional[int] = None,
        crash_report_timeout: Optional[int] = None,
        tags: Optional[Dict[str, str]] = None,
        wait_for_running: bool = False,
        wait_for_files: Optional[List[ContainerType]] = None,
        extra_files: Optional[List[File]] = None,
        existing_inputs: Optional[Container] = None,
        readonly_inputs: Optional[Container] = None,
        dryrun: bool = False,
        notification_config: Optional[NotificationConfig] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        ensemble_sync_delay: Optional[int] = None,
        colocate_all_tasks: bool = False,
        colocate_secondary_tasks: bool = True,
        check_fuzzer_help: bool = True,
        expect_crash_on_failure: bool = False,
        minimized_stack_depth: Optional[int] = None,
        coverage_filter: Optional[File] = None,
    ) -> Optional[Job]:
        """
        Basic libfuzzer job

        :param bool ensemble_sync_delay: Specify duration between
            syncing inputs during ensemble fuzzing (0 to disable).
        """

        # verify containers exist
        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)

        if readonly_inputs:
            self.onefuzz.containers.get(readonly_inputs)

        if dryrun:
            return None

        self.logger.info("creating libfuzzer from template")

        self._check_is_libfuzzer(target_exe)

        helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            name,
            build,
            duration,
            pool_name=pool_name,
            target_exe=target_exe,
        )

        helper.add_tags(tags)
        helper.define_containers(
            ContainerType.setup,
            ContainerType.inputs,
            ContainerType.crashes,
            ContainerType.reports,
            ContainerType.unique_reports,
            ContainerType.unique_inputs,
            ContainerType.no_repro,
            ContainerType.coverage,
            ContainerType.unique_inputs,
            ContainerType.regression_reports,
        )

        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)
            helper.containers[ContainerType.inputs] = existing_inputs
        else:
            helper.define_containers(ContainerType.inputs)

        if readonly_inputs:
            self.onefuzz.containers.get(readonly_inputs)
            helper.containers[ContainerType.readonly_inputs] = readonly_inputs

        helper.create_containers()
        helper.setup_notifications(notification_config)

        helper.upload_setup(setup_dir, target_exe, extra_files)
        if inputs:
            helper.upload_inputs(inputs)
        helper.wait_on(wait_for_files, wait_for_running)

        target_exe_blob_name = helper.setup_relative_blob_name(
            target_exe, setup_dir)

        if coverage_filter:
            coverage_filter_blob_name: Optional[
                str] = helper.setup_relative_blob_name(coverage_filter,
                                                       setup_dir)
        else:
            coverage_filter_blob_name = None

        self._create_tasks(
            job=helper.job,
            containers=helper.containers,
            pool_name=pool_name,
            target_exe=target_exe_blob_name,
            vm_count=vm_count,
            reboot_after_setup=reboot_after_setup,
            duration=duration,
            target_workers=target_workers,
            target_options=target_options,
            fuzzing_target_options=fuzzing_target_options,
            target_env=target_env,
            tags=helper.tags,
            crash_report_timeout=crash_report_timeout,
            check_retry_count=check_retry_count,
            debug=debug,
            ensemble_sync_delay=ensemble_sync_delay,
            colocate_all_tasks=colocate_all_tasks,
            colocate_secondary_tasks=colocate_secondary_tasks,
            check_fuzzer_help=check_fuzzer_help,
            expect_crash_on_failure=expect_crash_on_failure,
            minimized_stack_depth=minimized_stack_depth,
            coverage_filter=coverage_filter_blob_name,
        )

        self.logger.info("done creating tasks")
        helper.wait()
        return helper.job
Esempio n. 9
0
    def libfuzzer(
        self,
        project: str,
        build: str,
        pool_name: str,
        duration: int = 24,
        tags: Optional[Dict[str, str]] = None,
        dryrun: bool = False,
        max_target_count: int = 20,
        sync_inputs: bool = False,
        notification_config: Optional[NotificationConfig] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        ensemble_sync_delay: Optional[int] = None,
    ) -> None:
        """
        OssFuzz style libfuzzer jobs

        :param bool ensemble_sync_delay: Specify duration between
            syncing inputs during ensemble fuzzing (0 to disable).
        """

        fuzzers = sorted(glob.glob("*fuzzer"))
        if fuzzers:
            platform = OS.linux
        else:
            platform = OS.windows
            fuzzers = sorted(glob.glob("*fuzzer.exe"))

        if dryrun:
            return

        containers = self._containers(project, build, platform)
        container_sas = {}
        for name in containers:
            self.logger.info("creating container: %s", name)
            sas_url = self.onefuzz.containers.create(containers[name]).sas_url
            container_sas[name] = sas_url

        self.logger.info("uploading build artifacts")
        subprocess.check_output(
            [
                "azcopy",
                "sync",
                ".",
                container_sas["build"],
                "--exclude-pattern",
                "*fuzzer_seed_corpus.zip",
            ]
        )
        subprocess.check_output(
            [
                "azcopy",
                "sync",
                ".",
                container_sas["base"],
                '--include-pattern="*.so;*.dll;*.sh;*.ps1',
            ]
        )

        if max_target_count:
            fuzzers = fuzzers[:max_target_count]

        base_helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            build,
            "base",
            duration,
            pool_name=pool_name,
            target_exe=File(fuzzers[0]),
        )
        base_helper.platform = platform

        helpers = []
        for fuzzer in [File(x) for x in fuzzers]:
            fuzzer_name = fuzzer.replace(".exe", "").replace("_fuzzer", "")
            self.logger.info("creating tasks for %s", fuzzer)
            self.onefuzz.template.libfuzzer._check_is_libfuzzer(fuzzer)
            helper = JobHelper(
                self.onefuzz,
                self.logger,
                project,
                fuzzer_name,
                build,
                duration,
                job=base_helper.job,
                pool_name=pool_name,
                target_exe=fuzzer,
            )
            helper.platform = platform
            helper.add_tags(tags)
            helper.platform = base_helper.platform
            helper.job = base_helper.job
            helper.define_containers(
                ContainerType.setup,
                ContainerType.inputs,
                ContainerType.crashes,
                ContainerType.reports,
                ContainerType.unique_reports,
                ContainerType.no_repro,
                ContainerType.coverage,
            )
            helper.create_containers()
            helper.setup_notifications(notification_config)

            dst_sas = self.onefuzz.containers.get(
                helper.containers[ContainerType.setup]
            ).sas_url
            self._copy_exe(container_sas["build"], dst_sas, File(fuzzer))
            self._copy_all(container_sas["base"], dst_sas)

            zip_name = fuzzer.replace(".exe", "").replace("_fuzzer", "_seed_corpus.zip")

            if os.path.exists(zip_name) and sync_inputs:
                self.logger.info("uploading seeds")
                helper.upload_inputs_zip(File(zip_name))

            owners_path = File("%s.msowners" % fuzzer.replace(".exe", ""))
            options_path = File("%s.options" % fuzzer.replace(".exe", ""))

            target_env, target_options = self._options(options_path)
            helper.add_tags(self._owners(owners_path))

            # All fuzzers are copied to the setup container root.
            #
            # Cast because `glob()` returns `str`.
            fuzzer_blob_name = helper.target_exe_blob_name(fuzzer, None)

            self.onefuzz.template.libfuzzer._create_tasks(
                job=base_helper.job,
                containers=helper.containers,
                pool_name=pool_name,
                target_exe=fuzzer_blob_name,
                vm_count=VM_COUNT,
                duration=duration,
                target_options=target_options,
                target_env=target_env,
                tags=helper.tags,
                debug=debug,
                ensemble_sync_delay=ensemble_sync_delay,
            )
            helpers.append(helper)
        base_helper.wait()
Esempio n. 10
0
    def _create_job(
        self,
        task_type: TaskType,
        project: str,
        name: str,
        build: str,
        pool_name: PoolName,
        *,
        crashes: Optional[List[File]] = None,
        reports: Optional[List[str]] = None,
        target_exe: File = File("fuzz.exe"),
        tags: Optional[Dict[str, str]] = None,
        notification_config: Optional[NotificationConfig] = None,
        target_env: Optional[Dict[str, str]] = None,
        setup_dir: Optional[Directory] = None,
        reboot_after_setup: bool = False,
        target_options: Optional[List[str]] = None,
        dryrun: bool = False,
        duration: int = 24,
        crash_report_timeout: Optional[int] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        check_retry_count: Optional[int] = None,
        check_fuzzer_help: bool = True,
        delete_input_container: bool = True,
        check_regressions: bool = False,
    ) -> None:

        if dryrun:
            return None

        self.logger.info("creating regression task from template")

        helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            name,
            build,
            duration,
            pool_name=pool_name,
            target_exe=target_exe,
        )

        helper.define_containers(
            ContainerType.setup,
            ContainerType.crashes,
            ContainerType.reports,
            ContainerType.no_repro,
            ContainerType.unique_reports,
            ContainerType.regression_reports,
        )

        containers = [
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            (ContainerType.reports, helper.containers[ContainerType.reports]),
            (ContainerType.no_repro,
             helper.containers[ContainerType.no_repro]),
            (
                ContainerType.unique_reports,
                helper.containers[ContainerType.unique_reports],
            ),
            (
                ContainerType.regression_reports,
                helper.containers[ContainerType.regression_reports],
            ),
        ]

        if crashes:
            helper.containers[
                ContainerType.
                readonly_inputs] = helper.get_unique_container_name(
                    ContainerType.readonly_inputs)
            containers.append((
                ContainerType.readonly_inputs,
                helper.containers[ContainerType.readonly_inputs],
            ))

        helper.create_containers()
        if crashes:
            for file in crashes:
                self.onefuzz.containers.files.upload_file(
                    helper.containers[ContainerType.readonly_inputs], file)

        helper.setup_notifications(notification_config)

        helper.upload_setup(setup_dir, target_exe)
        target_exe_blob_name = helper.target_exe_blob_name(
            target_exe, setup_dir)

        self.logger.info("creating regression task")
        task = self.onefuzz.tasks.create(
            helper.job.job_id,
            task_type,
            target_exe_blob_name,
            containers,
            pool_name=pool_name,
            duration=duration,
            vm_count=1,
            reboot_after_setup=reboot_after_setup,
            target_options=target_options,
            target_env=target_env,
            tags=tags,
            target_timeout=crash_report_timeout,
            check_retry_count=check_retry_count,
            debug=debug,
            check_fuzzer_help=check_fuzzer_help,
            report_list=reports,
        )
        helper.wait_for_stopped = check_regressions

        self.logger.info("done creating tasks")
        helper.wait()

        if check_regressions:
            task = self.onefuzz.tasks.get(task.task_id)
            if task.error:
                raise Exception("task failed: %s", task.error)

            container = helper.containers[ContainerType.regression_reports]
            for filename in self.onefuzz.containers.files.list(
                    container).files:
                self.logger.info("checking file: %s", filename)
                if self._check_regression(container, File(filename)):
                    raise Exception(f"regression identified: {filename}")
            self.logger.info("no regressions")

        if (delete_input_container
                and ContainerType.readonly_inputs in helper.containers):
            helper.delete_container(
                helper.containers[ContainerType.readonly_inputs])
Esempio n. 11
0
    def basic(
        self,
        project: str,
        name: str,
        build: str,
        *,
        pool_name: str,
        target_exe: File = File("fuzz.exe"),
        setup_dir: Optional[Directory] = None,
        vm_count: int = 2,
        inputs: Optional[Directory] = None,
        reboot_after_setup: bool = False,
        duration: int = 24,
        generator_exe: Optional[str] = None,
        target_options: List[str] = ["{input}"],
        target_env: Optional[Dict[str, str]] = None,
        tags: Optional[Dict[str, str]] = None,
        wait_for_running: bool = False,
        wait_for_files: Optional[List[ContainerType]] = None,
        generator_options: Optional[List[str]] = None,
        radamsa_seed: Optional[int] = None,
        analyzer_exe: Optional[str] = None,
        analyzer_options: Optional[List[str]] = None,
        analyzer_env: Optional[Dict[str, str]] = None,
        existing_inputs: Optional[Container] = None,
        check_asan_log: bool = False,
        check_retry_count: Optional[int] = None,
        disable_check_debugger: bool = False,
        dryrun: bool = False,
        notification_config: Optional[NotificationConfig] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        ensemble_sync_delay: Optional[int] = None,
    ) -> Optional[Job]:
        """
        Basic radamsa job

        :param bool ensemble_sync_delay: Specify duration between
            syncing inputs during ensemble fuzzing (0 to disable).
        """

        if inputs is None and existing_inputs is None:
            raise Exception("radamsa requires inputs")

        if dryrun:
            return None

        # disable ensemble sync if only one VM is used
        if ensemble_sync_delay is None and vm_count == 1:
            ensemble_sync_delay = 0

        self.logger.info("creating radamsa from template")

        helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            name,
            build,
            duration,
            pool_name=pool_name,
            target_exe=target_exe,
        )

        helper.add_tags(tags)
        helper.define_containers(
            ContainerType.setup,
            ContainerType.crashes,
            ContainerType.reports,
            ContainerType.unique_reports,
            ContainerType.no_repro,
            ContainerType.analysis,
        )
        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)
            helper.containers[ContainerType.readonly_inputs] = existing_inputs
        else:
            helper.define_containers(ContainerType.readonly_inputs)
        helper.create_containers()
        helper.setup_notifications(notification_config)

        helper.upload_setup(setup_dir, target_exe)
        if inputs:
            helper.upload_inputs(inputs, read_only=True)
        helper.wait_on(wait_for_files, wait_for_running)

        if (
            len(
                self.onefuzz.containers.files.list(
                    helper.containers[ContainerType.readonly_inputs]
                ).files
            )
            == 0
        ):
            raise Exception("Radamsa requires at least one input file")

        target_exe_blob_name = helper.target_exe_blob_name(target_exe, setup_dir)

        tools = Container(
            "radamsa-linux" if helper.platform == OS.linux else "radamsa-win64"
        )
        if generator_exe is None:
            generator_exe = (
                "{tools_dir}/radamsa"
                if helper.platform == OS.linux
                else "{tools_dir}\\radamsa.exe"
            )
        rename_output = True
        if generator_options is None:
            generator_options, rename_output = (
                [
                    "-H",
                    "sha256",
                    "-o",
                    "{generated_inputs}/input-%h.%s",
                    "-n",
                    "100",
                    "-r",
                    "{input_corpus}",
                ],
                False,
            )

        if radamsa_seed is not None:
            generator_options += ["--seed", str(radamsa_seed)]

        self.logger.info("creating radamsa task")

        containers = [
            (ContainerType.tools, tools),
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            (
                ContainerType.readonly_inputs,
                helper.containers[ContainerType.readonly_inputs],
            ),
        ]

        fuzzer_task = self.onefuzz.tasks.create(
            helper.job.job_id,
            TaskType.generic_generator,
            target_exe_blob_name,
            containers,
            pool_name=pool_name,
            duration=duration,
            vm_count=vm_count,
            reboot_after_setup=reboot_after_setup,
            target_options=target_options,
            target_env=target_env,
            generator_exe=generator_exe,
            generator_options=generator_options,
            check_asan_log=check_asan_log,
            check_debugger=not disable_check_debugger,
            tags=helper.tags,
            rename_output=rename_output,
            debug=debug,
            ensemble_sync_delay=ensemble_sync_delay,
        )

        report_containers = [
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            (ContainerType.reports, helper.containers[ContainerType.reports]),
            (
                ContainerType.unique_reports,
                helper.containers[ContainerType.unique_reports],
            ),
            (ContainerType.no_repro, helper.containers[ContainerType.no_repro]),
        ]

        self.logger.info("creating generic_crash_report task")
        self.onefuzz.tasks.create(
            helper.job.job_id,
            TaskType.generic_crash_report,
            target_exe_blob_name,
            report_containers,
            duration=duration,
            vm_count=1,
            pool_name=pool_name,
            reboot_after_setup=reboot_after_setup,
            target_options=target_options,
            target_env=target_env,
            tags=helper.tags,
            check_asan_log=check_asan_log,
            check_debugger=not disable_check_debugger,
            check_retry_count=check_retry_count,
            prereq_tasks=[fuzzer_task.task_id],
            debug=debug,
        )

        if helper.platform == OS.windows:
            if analyzer_exe is None:
                analyzer_exe = "cdb.exe"
            if analyzer_options is None:
                analyzer_options = [
                    "-c",
                    "!analyze;q",
                    "-logo",
                    "{output_dir}\\{input_file_name_no_ext}.report",
                    "{target_exe}",
                    "{target_options}",
                ]

            self.logger.info("creating custom analysis")

            analysis_containers = [
                (ContainerType.setup, helper.containers[ContainerType.setup]),
                (ContainerType.tools, tools),
                (ContainerType.analysis, helper.containers[ContainerType.analysis]),
                (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            ]

            self.onefuzz.tasks.create(
                helper.job.job_id,
                TaskType.generic_analysis,
                target_exe_blob_name,
                analysis_containers,
                duration=duration,
                pool_name=pool_name,
                vm_count=vm_count,
                reboot_after_setup=reboot_after_setup,
                target_options=target_options,
                target_env=target_env,
                analyzer_exe=analyzer_exe,
                analyzer_options=analyzer_options,
                analyzer_env=analyzer_env,
                tags=helper.tags,
                prereq_tasks=[fuzzer_task.task_id],
                debug=debug,
            )

        self.logger.info("done creating tasks")
        helper.wait()
        return helper.job
Esempio n. 12
0
    def basic(
        self,
        project: str,
        name: str,
        build: str,
        *,
        pool_name: str,
        target_exe: File = File("fuzz.exe"),
        setup_dir: Optional[Directory] = None,
        vm_count: int = 2,
        inputs: Optional[Directory] = None,
        reboot_after_setup: bool = False,
        duration: int = 24,
        target_options: Optional[List[str]] = None,
        supervisor_exe: str = "{tools_dir}/afl-fuzz",
        supervisor_options: List[str] = [
            "-d",
            "-i",
            "{input_corpus}",
            "-o",
            "{runtime_dir}",
            "--",
            "{target_exe}",
            "{target_options}",
        ],
        supervisor_env: Optional[Dict[str, str]] = None,
        supervisor_input_marker: str = "@@",
        tags: Optional[Dict[str, str]] = None,
        wait_for_running: bool = False,
        wait_for_files: Optional[List[ContainerType]] = None,
        afl_container: Optional[Container] = None,
        existing_inputs: Optional[Container] = None,
        dryrun: bool = False,
        notification_config: Optional[NotificationConfig] = None,
        debug: Optional[List[TaskDebugFlag]] = None,
        ensemble_sync_delay: Optional[int] = None,
    ) -> Optional[Job]:
        """
        Basic AFL job

        :param Container afl_container: Specify the AFL container to use in the job
        :param bool ensemble_sync_delay: Specify duration between
            syncing inputs during ensemble fuzzing (0 to disable).
        """

        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)

        if dryrun:
            return None

        # disable ensemble sync if only one VM is used
        if ensemble_sync_delay is None and vm_count == 1:
            ensemble_sync_delay = 0

        self.logger.info("creating afl from template")

        target_options = target_options or ["{input}"]

        helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            name,
            build,
            duration,
            pool_name=pool_name,
            target_exe=target_exe,
        )
        helper.add_tags(tags)
        helper.define_containers(
            ContainerType.setup,
            ContainerType.crashes,
            ContainerType.reports,
            ContainerType.unique_reports,
        )
        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)
            helper.containers[ContainerType.inputs] = existing_inputs
        else:
            helper.define_containers(ContainerType.inputs)

        helper.create_containers()
        helper.setup_notifications(notification_config)
        helper.upload_setup(setup_dir, target_exe)
        if inputs:
            helper.upload_inputs(inputs)
        helper.wait_on(wait_for_files, wait_for_running)

        if (len(
                self.onefuzz.containers.files.list(
                    helper.containers[ContainerType.inputs]).files) == 0):
            raise Exception("AFL requires at least one input")

        target_exe_blob_name = helper.target_exe_blob_name(
            target_exe, setup_dir)

        if afl_container is None:
            afl_container = Container("afl-linux" if helper.platform ==
                                      OS.linux else "afl-windows")

        # verify the AFL container exists
        self.onefuzz.containers.get(afl_container)

        containers = [
            (ContainerType.tools, afl_container),
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            (ContainerType.inputs, helper.containers[ContainerType.inputs]),
        ]

        self.logger.info("creating afl fuzz task")
        fuzzer_task = self.onefuzz.tasks.create(
            helper.job.job_id,
            TaskType.generic_supervisor,
            target_exe_blob_name,
            containers,
            pool_name=pool_name,
            duration=duration,
            vm_count=vm_count,
            reboot_after_setup=reboot_after_setup,
            target_options=target_options,
            supervisor_exe=supervisor_exe,
            supervisor_options=supervisor_options,
            supervisor_env=supervisor_env,
            supervisor_input_marker=supervisor_input_marker,
            stats_file="{runtime_dir}/fuzzer_stats",
            stats_format=StatsFormat.AFL,
            task_wait_for_files=ContainerType.inputs,
            tags=helper.tags,
            debug=debug,
            ensemble_sync_delay=ensemble_sync_delay,
        )

        report_containers = [
            (ContainerType.setup, helper.containers[ContainerType.setup]),
            (ContainerType.crashes, helper.containers[ContainerType.crashes]),
            (ContainerType.reports, helper.containers[ContainerType.reports]),
            (
                ContainerType.unique_reports,
                helper.containers[ContainerType.unique_reports],
            ),
        ]

        self.logger.info("creating generic_crash_report task")
        self.onefuzz.tasks.create(
            helper.job.job_id,
            TaskType.generic_crash_report,
            target_exe_blob_name,
            report_containers,
            pool_name=pool_name,
            duration=duration,
            vm_count=1,
            reboot_after_setup=reboot_after_setup,
            target_options=target_options,
            check_debugger=True,
            tags=tags,
            prereq_tasks=[fuzzer_task.task_id],
            debug=debug,
        )

        self.logger.info("done creating tasks")
        helper.wait()
        return helper.job
Esempio n. 13
0
    def launch(self, path: str) -> None:
        """ Launch all of the fuzzing templates """

        for target, config in TARGETS.items():
            if target not in self.targets:
                continue

            if config.os not in self.os:
                continue

            self.logger.info("launching: %s", target)

            setup = Directory(os.path.join(
                path, target)) if config.use_setup else None
            target_exe = File(os.path.join(path, target, config.target_exe))
            inputs = (Directory(os.path.join(path, target, config.inputs))
                      if config.inputs else None)

            job: Optional[Job] = None
            if config.template == TemplateType.libfuzzer:
                job = self.of.template.libfuzzer.basic(
                    self.project,
                    target,
                    BUILD,
                    self.pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    duration=1,
                    vm_count=1,
                )
            elif config.template == TemplateType.radamsa:
                job = self.of.template.radamsa.basic(
                    self.project,
                    target,
                    BUILD,
                    pool_name=self.pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    check_asan_log=config.check_asan_log or False,
                    disable_check_debugger=config.disable_check_debugger
                    or False,
                    duration=1,
                    vm_count=1,
                )
            elif config.template == TemplateType.afl:
                job = self.of.template.afl.basic(
                    self.project,
                    target,
                    BUILD,
                    pool_name=self.pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    duration=1,
                    vm_count=1,
                )
            else:
                raise NotImplementedError

            if not job:
                raise Exception("missing job")

            self.containers[job.job_id] = []
            for task in self.of.tasks.list(job_id=job.job_id):
                self.tasks[task.task_id] = job.job_id
                self.containers[job.job_id] += [
                    ContainerWrapper(self.of.containers.get(x.name).sas_url)
                    for x in task.config.containers
                    if x.type in TARGETS[job.config.name].wait_for_files
                ]
            self.jobs[job.job_id] = job
            self.target_jobs[job.job_id] = target
Esempio n. 14
0
    def launch(self,
               path: Directory,
               *,
               os_list: List[OS],
               targets: List[str],
               duration=int) -> None:
        """ Launch all of the fuzzing templates """
        for target, config in TARGETS.items():
            if target not in targets:
                continue

            if config.os not in os_list:
                continue

            self.logger.info("launching: %s", target)

            setup = Directory(os.path.join(
                path, target)) if config.use_setup else None
            target_exe = File(os.path.join(path, target, config.target_exe))
            inputs = (Directory(os.path.join(path, target, config.inputs))
                      if config.inputs else None)

            if setup and config.nested_setup_dir:
                setup = Directory(os.path.join(setup, config.nested_setup_dir))

            job: Optional[Job] = None
            if config.template == TemplateType.libfuzzer:
                job = self.of.template.libfuzzer.basic(
                    self.project,
                    target,
                    BUILD,
                    self.pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    duration=duration,
                    vm_count=1,
                    reboot_after_setup=config.reboot_after_setup or False,
                )
            elif config.template == TemplateType.libfuzzer_dotnet:
                if setup is None:
                    raise Exception("setup required for libfuzzer_dotnet")
                job = self.of.template.libfuzzer.dotnet(
                    self.project,
                    target,
                    BUILD,
                    self.pools[config.os].name,
                    target_harness=config.target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    duration=duration,
                    vm_count=1,
                )
            elif config.template == TemplateType.libfuzzer_qemu_user:
                job = self.of.template.libfuzzer.qemu_user(
                    self.project,
                    target,
                    BUILD,
                    self.pools[config.os].name,
                    inputs=inputs,
                    target_exe=target_exe,
                    duration=duration,
                    vm_count=1,
                )
            elif config.template == TemplateType.radamsa:
                job = self.of.template.radamsa.basic(
                    self.project,
                    target,
                    BUILD,
                    pool_name=self.pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    check_asan_log=config.check_asan_log or False,
                    disable_check_debugger=config.disable_check_debugger
                    or False,
                    duration=duration,
                    vm_count=1,
                )
            elif config.template == TemplateType.afl:
                job = self.of.template.afl.basic(
                    self.project,
                    target,
                    BUILD,
                    pool_name=self.pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    duration=duration,
                    vm_count=1,
                )
            else:
                raise NotImplementedError

            if not job:
                raise Exception("missing job")
Esempio n. 15
0
    def basic(
        self,
        project: str,
        name: str,
        build: str,
        pool_name: str,
        *,
        target_exe: File = File("fuzz.exe"),
        setup_dir: Optional[Directory] = None,
        vm_count: int = 2,
        inputs: Optional[Directory] = None,
        reboot_after_setup: bool = False,
        duration: int = 24,
        target_workers: Optional[int] = None,
        target_options: Optional[List[str]] = None,
        target_env: Optional[Dict[str, str]] = None,
        check_retry_count: Optional[int] = None,
        crash_report_timeout: Optional[int] = None,
        tags: Optional[Dict[str, str]] = None,
        wait_for_running: bool = False,
        wait_for_files: Optional[List[ContainerType]] = None,
        extra_files: Optional[List[File]] = None,
        existing_inputs: Optional[Container] = None,
        dryrun: bool = False,
        notification_config: Optional[NotificationConfig] = None,
    ) -> None:
        """ Basic libfuzzer job """

        # verify containers exist
        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)

        if dryrun:
            return

        self.logger.info("creating libfuzzer from template")

        self._check_is_libfuzzer(target_exe)

        helper = JobHelper(
            self.onefuzz,
            self.logger,
            project,
            name,
            build,
            duration,
            pool_name=pool_name,
            target_exe=target_exe,
        )

        helper.add_tags(tags)
        helper.define_containers(
            ContainerType.setup,
            ContainerType.inputs,
            ContainerType.crashes,
            ContainerType.reports,
            ContainerType.unique_reports,
            ContainerType.no_repro,
            ContainerType.coverage,
        )

        if existing_inputs:
            self.onefuzz.containers.get(existing_inputs)
            helper.containers[ContainerType.inputs] = existing_inputs
        else:
            helper.define_containers(ContainerType.inputs)

        helper.create_containers()
        helper.setup_notifications(notification_config)

        helper.upload_setup(setup_dir, target_exe, extra_files)
        if inputs:
            helper.upload_inputs(inputs)
        helper.wait_on(wait_for_files, wait_for_running)

        target_exe_blob_name = helper.target_exe_blob_name(
            target_exe, setup_dir)

        self._create_tasks(
            job=helper.job,
            containers=helper.containers,
            pool_name=pool_name,
            target_exe=target_exe_blob_name,
            vm_count=vm_count,
            reboot_after_setup=reboot_after_setup,
            duration=duration,
            target_workers=target_workers,
            target_options=target_options,
            target_env=target_env,
            tags=helper.tags,
            crash_report_timeout=crash_report_timeout,
            check_retry_count=check_retry_count,
        )

        self.logger.info("done creating tasks")
        helper.wait()
    def test_path_resolution(self) -> None:
        helper = JobHelper(
            Onefuzz(),
            logging.getLogger(),
            "a",
            "b",
            "c",
            False,
            target_exe=File("README.md"),
            job=Job(
                job_id=UUID("0" * 32),
                state=JobState.init,
                config=JobConfig(project="a", name="a", build="a", duration=1),
            ),
        )
        values = {
            (File("filename.txt"), None): "filename.txt",
            (File("dir/filename.txt"), None): "filename.txt",
            (File("./filename.txt"), None): "filename.txt",
            (File("./filename.txt"), Directory(".")): "filename.txt",
            (File("dir/filename.txt"), Directory("dir")): "filename.txt",
            (File("dir/filename.txt"), Directory("dir/")): "filename.txt",
            (File("dir/filename.txt"), Directory("./dir")): "filename.txt",
            (File("./dir/filename.txt"), Directory("./dir/")): "filename.txt",
        }

        expected = "filename.txt"
        if sys.platform == "linux":
            filename = File("/unused/filename.txt")
            values[(filename, None)] = expected
            values[(filename, Directory("/unused"))] = expected
            values[(filename, Directory("/unused/"))] = expected

        if sys.platform == "windows":
            for filename in [
                    File("c:/unused/filename.txt"),
                    File("c:\\unused/filename.txt"),
                    File("c:\\unused\\filename.txt"),
            ]:
                values[(filename, None)] = expected
                values[(filename, Directory("c:/unused"))] = expected
                values[(filename, Directory("c:/unused/"))] = expected
                values[(filename, Directory("c:\\unused\\"))] = expected
                values[(filename, Directory("c:\\unused\\"))] = expected

        for (args, expected) in values.items():
            self.assertEqual(helper.target_exe_blob_name(*args), expected)

        with self.assertRaises(ValueError):
            helper.target_exe_blob_name(File("dir/filename.txt"),
                                        Directory("other_dir"))
Esempio n. 17
0
    def launch(self,
               path: Directory,
               *,
               os_list: List[OS],
               targets: List[str],
               duration=int) -> List[UUID]:
        """Launch all of the fuzzing templates"""

        pools = {}
        for pool in self.of.pools.list():
            pools[pool.os] = pool

        job_ids = []

        for target, config in TARGETS.items():
            if target not in targets:
                continue

            if config.os not in os_list:
                continue

            if config.os not in pools.keys():
                raise Exception(
                    f"No pool for target: {target} ,os: {config.os}")

            self.logger.info("launching: %s", target)

            setup = Directory(os.path.join(
                path, target)) if config.use_setup else None
            target_exe = File(os.path.join(path, target, config.target_exe))
            inputs = (Directory(os.path.join(path, target, config.inputs))
                      if config.inputs else None)

            if setup and config.nested_setup_dir:
                setup = Directory(os.path.join(setup, config.nested_setup_dir))

            job: Optional[Job] = None
            if config.template == TemplateType.libfuzzer:
                job = self.of.template.libfuzzer.basic(
                    self.project,
                    target,
                    BUILD,
                    pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    duration=duration,
                    vm_count=1,
                    reboot_after_setup=config.reboot_after_setup or False,
                    target_options=config.target_options,
                )
            elif config.template == TemplateType.libfuzzer_dotnet:
                if setup is None:
                    raise Exception("setup required for libfuzzer_dotnet")
                job = self.of.template.libfuzzer.dotnet(
                    self.project,
                    target,
                    BUILD,
                    pools[config.os].name,
                    target_harness=config.target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    duration=duration,
                    vm_count=1,
                    target_options=config.target_options,
                )
            elif config.template == TemplateType.libfuzzer_qemu_user:
                job = self.of.template.libfuzzer.qemu_user(
                    self.project,
                    target,
                    BUILD,
                    pools[config.os].name,
                    inputs=inputs,
                    target_exe=target_exe,
                    duration=duration,
                    vm_count=1,
                    target_options=config.target_options,
                )
            elif config.template == TemplateType.radamsa:
                job = self.of.template.radamsa.basic(
                    self.project,
                    target,
                    BUILD,
                    pool_name=pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    check_asan_log=config.check_asan_log or False,
                    disable_check_debugger=config.disable_check_debugger
                    or False,
                    duration=duration,
                    vm_count=1,
                )
            elif config.template == TemplateType.afl:
                job = self.of.template.afl.basic(
                    self.project,
                    target,
                    BUILD,
                    pool_name=pools[config.os].name,
                    target_exe=target_exe,
                    inputs=inputs,
                    setup_dir=setup,
                    duration=duration,
                    vm_count=1,
                    target_options=config.target_options,
                )
            else:
                raise NotImplementedError

            if config.inject_fake_regression and job is not None:
                self.of.debug.notification.job(job.job_id)

            if not job:
                raise Exception("missing job")

            job_ids.append(job.job_id)

        return job_ids