def do_bisect(bisect_type, source_id, project_name, engine, sanitizer, architecture, fuzz_target, old_commit, new_commit, testcase): """Do the actual bisect.""" import bisector import build_specified_commit with tempfile.NamedTemporaryFile() as f: f.write(testcase) f.flush() build_data = build_specified_commit.BuildData( project_name=project_name, engine=engine, sanitizer=sanitizer, architecture=architecture) try: result = bisector.bisect(bisect_type, old_commit, new_commit, f.name, fuzz_target, build_data) except bisector.BisectError as e: logging.error('Bisect failed with exception:\n%s', traceback.format_exc()) return bisector.Result(e.repo_url, None) except Exception: logging.error('Bisect failed with unexpected exception:\n%s', traceback.format_exc()) return None if result.commit == old_commit: logging.error('Bisect failed for testcase %s, bisected to old_commit', source_id) result = None return result
def test_build_fuzzers_from_commit(self): """Tests if the fuzzers can build at a proper 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. """ test_data = os.path.join(TEST_DIR_PATH, 'testcases', 'yara_test_data') with tempfile.TemporaryDirectory() as tmp_dir: project_name = 'yara' old_commit = 'f79be4f2330f4b89ea2f42e1c44ca998c59a0c0f' new_commit = 'f50a39051ea8c7f10d6d8db9656658b49601caef' fuzzer = 'rules_fuzzer' yara_repo_manager = repo_manager.RepoManager( 'https://github.com/VirusTotal/yara.git', tmp_dir, repo_name='yara') build_data = build_specified_commit.BuildData() build_data.sanitizer = 'address' build_data.architecture = 'x86_64' build_data.engine = 'libfuzzer' build_data.project_name = 'yara' build_specified_commit.build_fuzzers_from_commit( old_commit, yara_repo_manager, build_data) old_error_code = helper.reproduce_impl(project_name, fuzzer, False, [], [], test_data) build_specified_commit.build_fuzzers_from_commit( new_commit, yara_repo_manager, build_data) new_error_code = helper.reproduce_impl(project_name, fuzzer, False, [], [], test_data) self.assertNotEqual(new_error_code, old_error_code)
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 main(): """Finds the commit SHA where an error was initally introduced.""" logging.getLogger().setLevel(logging.INFO) utils.chdir_to_root() parser = argparse.ArgumentParser( description='git bisection for finding introduction of bugs') parser.add_argument('--project_name', help='The name of the project where the bug occurred.', required=True) parser.add_argument('--new_commit', help='The newest commit SHA to be bisected.', required=True) parser.add_argument('--old_commit', help='The oldest commit SHA to be bisected.', required=True) parser.add_argument('--fuzz_target', help='The name of the fuzzer to be built.', required=True) parser.add_argument('--test_case_path', help='The path to test case.', required=True) parser.add_argument('--engine', help='The default is "libfuzzer".', default='libfuzzer') parser.add_argument('--sanitizer', default='address', help='The default is "address".') parser.add_argument('--type', choices=['regressed', 'fixed'], help='The bisection type.', required=True) parser.add_argument('--architecture', default='x86_64') args = parser.parse_args() build_data = build_specified_commit.BuildData( project_name=args.project_name, engine=args.engine, sanitizer=args.sanitizer, architecture=args.architecture) result = bisect(args.type, args.old_commit, args.new_commit, args.test_case_path, args.fuzz_target, build_data) if not result.commit: logging.error('No error was found in commit range %s:%s', args.old_commit, args.new_commit) return 1 if result.commit == args.old_commit: logging.error( 'Bisection Error: Both the first and the last commits in' 'the given range have the same behavior, bisection is not possible. ' ) return 1 if args.type == 'regressed': print('Error was introduced at commit %s' % result.commit) elif args.type == 'fixed': print('Error was fixed at commit %s' % result.commit) return 0
def test_bisect_invalid_repo(self): """Test the bisection method on a project that does not exist.""" test_repo = test_repos.INVALID_REPO build_data = build_specified_commit.BuildData( project_name=test_repo.project_name, engine='libfuzzer', sanitizer='address', architecture='x86_64') with self.assertRaises(ValueError): bisector.bisect(test_repo.old_commit, test_repo.new_commit, test_repo.test_case_path, test_repo.fuzz_target, build_data)
def main(): """Finds the commit SHA where an error was initally introduced.""" oss_fuzz_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) if os.getcwd() != oss_fuzz_dir: print('Changing directory to OSS-Fuzz home directory') os.chdir(oss_fuzz_dir) parser = argparse.ArgumentParser( description='git bisection for finding introduction of bugs') parser.add_argument('--project_name', help='The name of the project where the bug occurred.', required=True) parser.add_argument('--commit_new', help='The newest commit SHA to be bisected.', required=True) parser.add_argument('--commit_old', help='The oldest commit SHA to be bisected.', required=True) parser.add_argument('--fuzz_target', help='The name of the fuzzer to be built.', required=True) parser.add_argument('--testcase', help='The path to test case.', required=True) parser.add_argument('--engine', help='The default is "libfuzzer".', default='libfuzzer') parser.add_argument('--sanitizer', default='address', help='The default is "address".') parser.add_argument('--architecture', default='x86_64') args = parser.parse_args() build_data = build_specified_commit.BuildData( project_name=args.project_name, engine=args.engine, sanitizer=args.sanitizer, architecture=args.architecture) error_sha = bisect(args.commit_old, args.commit_new, args.testcase, args.fuzz_target, build_data) if not error_sha: print('No error was found in commit range %s:%s' % (args.commit_old, args.commit_new)) return 1 if error_sha == args.commit_old: print( 'Bisection Error: Both the first and the last commits in the given ' + 'range have the same behavior, bisection is not possible. ') return 1 print('Error was introduced at commit %s' % error_sha) return 0
def test_bisect_curl(self): """Test the bisect method on the curl project.""" build_data = build_specified_commit.BuildData(project_name='curl', engine='libfuzzer', sanitizer='address', architecture='x86_64') commit_new = 'dda418266c99ceab368d723facb52069cbb9c8d5' commit_old = 'df26f5f9c36e19cd503c0e462e9f72ad37b84c82' fuzz_target = 'curl_fuzzer_ftp' testcase = os.path.join(TEST_DIR_PATH, 'testcases', 'curl_test_data') error_sha = bisector.bisect(commit_old, commit_new, testcase, fuzz_target, build_data) self.assertEqual(error_sha, 'df26f5f9c36e19cd503c0e462e9f72ad37b84c82')
def test_bisect(self): """Test the bisect method on example projects.""" for test_repo in test_repos.TEST_REPOS: build_data = build_specified_commit.BuildData( project_name=test_repo.project_name, engine='libfuzzer', sanitizer='address', architecture='x86_64') error_sha = bisector.bisect(test_repo.old_commit, test_repo.new_commit, test_repo.test_case_path, test_repo.fuzz_target, build_data) self.assertEqual(error_sha, test_repo.intro_commit)
def test_bisect_usrsctp(self): """Test the bisect method on the usrsctp.""" build_data = build_specified_commit.BuildData(project_name='usrsctp', engine='libfuzzer', sanitizer='address', architecture='x86_64') commit_old = '4886aaa49fb90e479226fcfc3241d74208908232' commit_new = 'c710749b1053978179a027973a3ea3bccf20ee5c' testcase = os.path.join(TEST_DIR_PATH, 'testcases', 'usrsctp_test_data') fuzz_target = 'fuzzer_connect' error_sha = bisector.bisect(commit_old, commit_new, testcase, fuzz_target, build_data) self.assertEqual(error_sha, '457d6ead58e82584d9dcb826f6739347f59ebd3a')
def test_bisect_usrsctp_single_error_exists(self): """Tests what happens with a single with an error.""" build_data = build_specified_commit.BuildData(project_name='usrsctp', engine='libfuzzer', sanitizer='address', architecture='x86_64') commit_old = 'c710749b1053978179a027973a3ea3bccf20ee5c' commit_new = 'c710749b1053978179a027973a3ea3bccf20ee5c' testcase = os.path.join(TEST_DIR_PATH, 'testcases', 'usrsctp_test_data') fuzz_target = 'fuzzer_connect' error_sha = bisector.bisect(commit_old, commit_new, testcase, fuzz_target, build_data) self.assertEqual(error_sha, 'c710749b1053978179a027973a3ea3bccf20ee5c')
def test_bisect_libarchive(self): """Test the bisect method on libarchive.""" build_data = build_specified_commit.BuildData( project_name='libarchive', engine='libfuzzer', sanitizer='address', architecture='x86_64') commit_new = '458e49358f17ec58d65ab1c45cf299baaf3c98d1' commit_old = '5bd2a9b6658a3a6efa20bb9ad75bd39a44d71da6' fuzz_target = 'libarchive_fuzzer' testcase = os.path.join(TEST_DIR_PATH, 'testcases', 'libarchive_test_data') error_sha = bisector.bisect(commit_old, commit_new, testcase, fuzz_target, build_data) self.assertEqual(error_sha, '840266712006de5e737f8052db920dfea2be4260')
def test_bisect_invalid_repo(self): """Test the bisection method on a project that does not exist.""" build_data = build_specified_commit.BuildData( project_name='not-a-real-repo', engine='libfuzzer', sanitizer='address', architecture='x86_64') commit_old = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' commit_new = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' testcase = os.path.join(TEST_DIR_PATH, 'testcases', 'usrsctp_test_data') fuzz_target = 'fuzzer_connect' with self.assertRaises(ValueError): bisector.bisect(commit_old, commit_new, testcase, fuzz_target, build_data)
def test_bisect_usrsctp_single_no_error_exists(self): """Tests what happens with a single with an error.""" build_data = build_specified_commit.BuildData(project_name='usrsctp', engine='libfuzzer', sanitizer='address', architecture='x86_64') commit_old = '4886aaa49fb90e479226fcfc3241d74208908232' commit_new = '4886aaa49fb90e479226fcfc3241d74208908232' testcase = os.path.join(TEST_DIR_PATH, 'testcases', 'usrsctp_test_data') fuzz_target = 'fuzzer_connect' error_sha = bisector.bisect(commit_old, commit_new, testcase, fuzz_target, build_data) self.assertEqual(error_sha, '4886aaa49fb90e479226fcfc3241d74208908232')
def build_fuzzers(args): """Builds all of the fuzzers for a specific OSS-Fuzz project. Returns: True on success False on failure. """ # TODO: Fix return value bubble to actually handle errors. with tempfile.TemporaryDirectory() as tmp_dir: inferred_url, repo_name = build_specified_commit.detect_main_repo( args.project_name, repo_name=args.repo_name) build_repo_manager = repo_manager.RepoManager(inferred_url, tmp_dir, repo_name=repo_name) build_data = build_specified_commit.BuildData() build_data.project_name = args.project_name build_data.sanitizer = 'address' build_data.engine = 'libfuzzer' build_data.architecture = 'x86_64' return build_specified_commit.build_fuzzers_from_commit( args.commit_sha, build_repo_manager, build_data) == 0