def test_build_fuzzers_from_commit(self): """Tests if the fuzzers can build at a specified commit. This is done by using a known regression range for a specific test case. The old commit should show the error when its fuzzers run and the new one should not. """ with tempfile.TemporaryDirectory() as tmp_dir: test_case = test_repos.TEST_REPOS[1] self.assertTrue(helper.build_image_impl(test_case.project_name)) host_src_dir = build_specified_commit.copy_src_from_docker( test_case.project_name, tmp_dir) test_repo_manager = repo_manager.clone_repo_and_get_manager( test_case.git_url, host_src_dir, test_case.oss_repo_name) build_data = build_specified_commit.BuildData( sanitizer='address', architecture='x86_64', engine='libfuzzer', project_name=test_case.project_name) build_specified_commit.build_fuzzers_from_commit(test_case.old_commit, test_repo_manager, host_src_dir, build_data) old_error_code = helper.reproduce_impl(test_case.project_name, test_case.fuzz_target, False, [], [], test_case.test_case_path) build_specified_commit.build_fuzzers_from_commit(test_case.new_commit, test_repo_manager, host_src_dir, build_data) new_error_code = helper.reproduce_impl(test_case.project_name, test_case.fuzz_target, False, [], [], test_case.test_case_path) self.assertNotEqual(new_error_code, old_error_code)
def build_fuzzers_from_commit(commit, build_repo_manager, host_src_path, build_data): """Builds a OSS-Fuzz fuzzer at a specific commit SHA. Args: commit: The commit SHA to build the fuzzers at. build_repo_manager: The OSS-Fuzz project's repo manager to be built at. build_data: A struct containing project build information. Returns: 0 on successful build or error code on failure. """ oss_fuzz_repo_manager = repo_manager.BaseRepoManager(helper.OSS_FUZZ_DIR) num_retry = 1 for i in range(num_retry + 1): build_repo_manager.checkout_commit(commit, clean=False) result = helper.build_fuzzers_impl(project_name=build_data.project_name, clean=True, engine=build_data.engine, sanitizer=build_data.sanitizer, architecture=build_data.architecture, env_to_add=None, source_path=host_src_path, mount_location='/src') if result == 0 or i == num_retry: break # Retry with an OSS-Fuzz builder container that's closer to the project # commit date. commit_date = build_repo_manager.commit_date(commit) projects_dir = os.path.join('projects', build_data.project_name) # Find first change in the projects/<PROJECT> directory before the project # commit date. oss_fuzz_commit, _, _ = oss_fuzz_repo_manager.git([ 'log', '--before=' + commit_date.isoformat(), '-n1', '--format=%H', projects_dir ], check_result=True) oss_fuzz_commit = oss_fuzz_commit.strip() if not oss_fuzz_commit: logging.warning('No suitable earlier OSS-Fuzz commit found.') break logging.info('Build failed. Retrying on earlier OSS-Fuzz commit %s.', oss_fuzz_commit) # Check out projects/<PROJECT> dir to the commit that was found. oss_fuzz_repo_manager.git(['checkout', oss_fuzz_commit, projects_dir], check_result=True) # Rebuild image and re-copy src dir since things in /src could have changed. if not helper.build_image_impl(build_data.project_name): raise RuntimeError('Failed to rebuild image.') shutil.rmtree(host_src_path, ignore_errors=True) copy_src_from_docker(build_data.project_name, os.path.dirname(host_src_path)) return result == 0
def test_oss_fuzz_project(self, mock_docker_build): """Tests that build_image_impl works as intended with an OSS-Fuzz project.""" project_name = 'example' self.assertTrue(helper.build_image_impl(helper.Project(project_name))) build_dir = os.path.join(helper.OSS_FUZZ_DIR, 'projects', project_name) mock_docker_build.assert_called_with([ '-t', 'gcr.io/oss-fuzz/example', '--file', os.path.join(build_dir, 'Dockerfile'), build_dir ])
def test_base_image(self, mock_docker_build): """Tests that build_image_impl works as intended with a base-image.""" image_name = 'base-image' self.assertTrue(helper.build_image_impl(helper.Project(image_name))) build_dir = os.path.join(helper.OSS_FUZZ_DIR, 'infra/base-images/base-image') mock_docker_build.assert_called_with([ '-t', 'gcr.io/oss-fuzz-base/base-image', '--file', os.path.join(build_dir, 'Dockerfile'), build_dir ])
def detect_main_repo(project_name, repo_name=None, commit=None, src_dir='/src'): """Checks a docker image for the main repo of an OSS-Fuzz project. Note: The default is to use the repo name to detect the main repo. Args: project_name: The name of the oss-fuzz project. repo_name: The name of the main repo in an OSS-Fuzz project. commit: A commit SHA that is associated with the main repo. src_dir: The location of the projects source on the docker image. Returns: The repo's origin, the repo's name. """ # TODO: Add infra for non hardcoded '/src'. if not repo_name and not commit: print( 'Error: can not detect main repo without a repo_name or a commit.') return None, None if repo_name and commit: print( 'Both repo name and commit specific. Using repo name for detection.' ) helper.build_image_impl(project_name) docker_image_name = 'gcr.io/oss-fuzz/' + project_name command_to_run = [ 'docker', 'run', '--rm', '-t', docker_image_name, 'python3', os.path.join(src_dir, 'detect_repo.py'), '--src_dir', src_dir ] if repo_name: command_to_run.extend(['--repo_name', repo_name]) else: command_to_run.extend(['--example_commit', commit]) out, _ = execute(command_to_run) match = re.search(r'\bDetected repo: ([^ ]+) ([^ ]+)', out.rstrip()) if match and match.group(1) and match.group(2): return match.group(1), match.group(2) return None, None
def _build_image_with_retries(project_name): """Build image with retries.""" for _ in range(_IMAGE_BUILD_TRIES): result = helper.build_image_impl(project_name) if result: return result time.sleep(_IMAGE_BUILD_RETRY_SLEEP) return result
def detect_main_repo_from_docker(project_name, example_commit, src_dir='/src'): """Checks a docker image for the main repo of an OSS-Fuzz project. Args: project_name: The name of the OSS-Fuzz project example_commit: An associated commit SHA src_dir: The location of the projects source on the docker image Returns: The repo's origin, the repo's name """ helper.build_image_impl(project_name) docker_image_name = 'gcr.io/oss-fuzz/' + project_name command_to_run = [ 'docker', 'run', '--rm', '-i', '-t', docker_image_name, 'python3', os.path.join(src_dir, 'detect_repo.py'), '--src_dir', src_dir, '--example_commit', example_commit ] out, _ = execute(command_to_run) match = re.search(r'\bDetected repo: ([^ ]+) ([^ ]+)', out.rstrip()) if match and match.group(1) and match.group(2): return match.group(1), match.group(2).rstrip() return None, None
def test_external_project(self, mock_docker_build): """Tests that build_image_impl works as intended with a non-OSS-Fuzz project.""" with tempfile.TemporaryDirectory() as temp_dir: project_src_path = os.path.join(temp_dir, 'example') os.mkdir(project_src_path) build_integration_path = 'build-integration' project = helper.Project(project_src_path, is_external=True, build_integration_path=build_integration_path) self.assertTrue(helper.build_image_impl(project)) mock_docker_build.assert_called_with([ '-t', 'gcr.io/oss-fuzz/example', '--file', os.path.join(project_src_path, build_integration_path, 'Dockerfile'), project_src_path ])
def detect_main_repo(project_name, repo_name=None, commit=None): """Checks a docker image for the main repo of an OSS-Fuzz project. Note: The default is to use the repo name to detect the main repo. Args: project_name: The name of the oss-fuzz project. repo_name: The name of the main repo in an OSS-Fuzz project. commit: A commit SHA that is associated with the main repo. src_dir: The location of the projects source on the docker image. Returns: The repo's origin, the repo's path. """ if not repo_name and not commit: logging.error( 'Error: can not detect main repo without a repo_name or a commit.') return None, None if repo_name and commit: logging.info( 'Both repo name and commit specific. Using repo name for detection.' ) # Change to oss-fuzz main directory so helper.py runs correctly. utils.chdir_to_root() if not helper.build_image_impl(project_name): logging.error('Error: building %s image failed.', project_name) return None, None docker_image_name = 'gcr.io/oss-fuzz/' + project_name command_to_run = [ 'docker', 'run', '--rm', '-t', docker_image_name, 'python3', os.path.join('/opt', 'cifuzz', 'detect_repo.py') ] if repo_name: command_to_run.extend(['--repo_name', repo_name]) else: command_to_run.extend(['--example_commit', commit]) out, _, _ = utils.execute(command_to_run) match = re.search(r'\bDetected repo: ([^ ]+) ([^ ]+)', out.rstrip()) if match and match.group(1) and match.group(2): return match.group(1), match.group(2) logging.error('Failed to detect repo:\n%s', out) return None, None
def _build_image_with_retries(project_name): """Build image with retries.""" return helper.build_image_impl(project_name)
def test_pull(self, mock_pull_images, _): """Tests that pull=True is handled properly.""" image_name = 'base-image' project = helper.Project(image_name, is_external=True) self.assertTrue(helper.build_image_impl(project, pull=True)) mock_pull_images.assert_called_with('c++')
def test_no_cache(self, mock_docker_build): """Tests that cache=False is handled properly.""" image_name = 'base-image' helper.build_image_impl(helper.Project(image_name), cache=False) self.assertIn('--no-cache', mock_docker_build.call_args_list[0][0][0])
def test_pull(self, mock_pull_images, _): """Tests that pull=True is handled properly.""" image_name = 'base-image' self.assertTrue( helper.build_image_impl(helper.Project(image_name), pull=True)) mock_pull_images.assert_called_with()