diff options
-rw-r--r-- | docs/getting-started/continuous_integration.md | 62 | ||||
-rw-r--r-- | infra/cifuzz/actions/build_fuzzers/action.yml | 4 | ||||
-rw-r--r-- | infra/cifuzz/actions/build_fuzzers/build_fuzzers_entrypoint.py | 20 | ||||
-rw-r--r-- | infra/cifuzz/actions/run_fuzzers/action.yml | 3 | ||||
-rw-r--r-- | infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py | 9 | ||||
-rw-r--r-- | infra/cifuzz/cifuzz.py | 74 | ||||
-rw-r--r-- | infra/cifuzz/cifuzz_test.py | 144 | ||||
-rw-r--r-- | infra/cifuzz/fuzz_target.py | 18 | ||||
-rwxr-xr-x | infra/cifuzz/test_files/out/memory/out/curl_fuzzer_memory | bin | 0 -> 9768680 bytes | |||
-rwxr-xr-x | infra/cifuzz/test_files/out/undefined/out/curl_fuzzer_undefined | bin | 0 -> 14401312 bytes |
10 files changed, 291 insertions, 43 deletions
diff --git a/docs/getting-started/continuous_integration.md b/docs/getting-started/continuous_integration.md index aebc78a4..41aabf8e 100644 --- a/docs/getting-started/continuous_integration.md +++ b/docs/getting-started/continuous_integration.md @@ -81,6 +81,7 @@ jobs: ### Optional configuration +#### Configurable Variables `fuzz-time`: Determines how long CIFuzz spends fuzzing your project in seconds. The default is 600 seconds. The GitHub Actions max run time is 21600 seconds (6 hours). @@ -89,6 +90,67 @@ CIFuzz will never report a failure even if it finds a crash in your project. This requires the user to manually check the logs for detected bugs. If dry run mode is desired, make sure to set the dry-run parameters in both the `Build Fuzzers` and `Run Fuzzers` action step. +#### Adding Other Sanitizers +CIFuzz supports address, memory and undefined sanitizers. Address is the default +sanitizer and will be used for every job in which a sanitizer is not specified. +To add another sanitizer to your workflow copy the `Fuzzing` job and rename it +to the sanitizer you want to fuzz with. Then add the sanitizer variable to both +the `Build Fuzzers` step and the `Run Fuzzers` step. The choices are `'address'`, +`'memory'`, and `'undefined'`. Once this additional job is configured the CIFuzz +workflow will run all of the jobs corresponding to each sanitizer simultaneously. +It is important to note that the `Build Fuzzers` and the `Run Fuzzers` sanitizer +field needs to be the same. See the following main.yml file for an example. + +```yaml +name: CIFuzz +on: [pull_request] +jobs: + AddressFuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'example' + dry-run: false + # sanitizer: address + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'example' + fuzz-seconds: 600 + dry-run: false + # sanitizer: address + - name: Upload Crash + uses: actions/upload-artifact@v1 + if: failure() + with: + name: Address-Artifacts + path: ./out/artifacts + UndefinedFuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'example' + dry-run: false + sanitizer: 'undefined' + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'example' + fuzz-seconds: 600 + dry-run: false + sanitizer: 'undefined' + - name: Upload Crash + uses: actions/upload-artifact@v1 + if: failure() + with: + name: Undefined-Artifacts + path: ./out/artifacts +``` + ## Understanding results The results of CIFuzz can be found in two different places. diff --git a/infra/cifuzz/actions/build_fuzzers/action.yml b/infra/cifuzz/actions/build_fuzzers/action.yml index e9fb31ea..35c50f65 100644 --- a/infra/cifuzz/actions/build_fuzzers/action.yml +++ b/infra/cifuzz/actions/build_fuzzers/action.yml @@ -8,9 +8,13 @@ inputs: dry-run: description: 'If set, run the action without actually reporting a failure.' default: false + sanitizer: + description: 'The sanitizer to build the fuzzers with.' + default: 'address' runs: using: 'docker' image: 'Dockerfile' env: OSS_FUZZ_PROJECT_NAME: ${{ inputs.oss-fuzz-project-name }} DRY_RUN: ${{ inputs.dry-run}} + SANITIZER: $${{ inputs.sanitizer }} diff --git a/infra/cifuzz/actions/build_fuzzers/build_fuzzers_entrypoint.py b/infra/cifuzz/actions/build_fuzzers/build_fuzzers_entrypoint.py index 3c84e64a..9ae83fbf 100644 --- a/infra/cifuzz/actions/build_fuzzers/build_fuzzers_entrypoint.py +++ b/infra/cifuzz/actions/build_fuzzers/build_fuzzers_entrypoint.py @@ -43,6 +43,8 @@ def main(): GITHUB_REF: The pull request reference that triggered this script. GITHUB_EVENT_NAME: The name of the hook event that triggered this script. GITHUB_WORKSPACE: The shared volume directory where input artifacts are. + DRY_RUN: If true, no failures will surface. + SANITIZER: The sanitizer to use when running fuzzers. Returns: 0 on success or 1 on Failure. @@ -53,6 +55,7 @@ def main(): commit_sha = os.environ.get('GITHUB_SHA') event = os.environ.get('GITHUB_EVENT_NAME') workspace = os.environ.get('GITHUB_WORKSPACE') + sanitizer = os.environ.get('SANITIZER').lower() # Check if failures should not be reported. dry_run = (os.environ.get('DRY_RUN').lower() == 'true') @@ -67,19 +70,24 @@ def main(): logging.error('This script needs to be run in the Github action context.') return returncode - if event == 'push' and not cifuzz.build_fuzzers( - oss_fuzz_project_name, github_repo_name, workspace, - commit_sha=commit_sha): + if event == 'push' and not cifuzz.build_fuzzers(oss_fuzz_project_name, + github_repo_name, + workspace, + commit_sha=commit_sha, + sanitizer=sanitizer): logging.error('Error building fuzzers for project %s with commit %s.', oss_fuzz_project_name, commit_sha) return returncode - if event == 'pull_request' and not cifuzz.build_fuzzers( - oss_fuzz_project_name, github_repo_name, workspace, pr_ref=pr_ref): + if event == 'pull_request' and not cifuzz.build_fuzzers(oss_fuzz_project_name, + github_repo_name, + workspace, + pr_ref=pr_ref, + sanitizer=sanitizer): logging.error('Error building fuzzers for project %s with pull request %s.', oss_fuzz_project_name, pr_ref) return returncode out_dir = os.path.join(workspace, 'out') - if cifuzz.check_fuzzer_build(out_dir): + if cifuzz.check_fuzzer_build(out_dir, sanitizer=sanitizer): return 0 return returncode diff --git a/infra/cifuzz/actions/run_fuzzers/action.yml b/infra/cifuzz/actions/run_fuzzers/action.yml index ca40c4fe..b6614023 100644 --- a/infra/cifuzz/actions/run_fuzzers/action.yml +++ b/infra/cifuzz/actions/run_fuzzers/action.yml @@ -12,6 +12,9 @@ inputs: dry-run: description: 'If set, run the action without actually reporting a failure.' default: false + sanitizer: + description: 'The sanitizer to run the fuzzers with.' + default: 'address' runs: using: 'docker' image: 'Dockerfile' diff --git a/infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py b/infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py index 4d80e5e7..7f39388f 100644 --- a/infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py +++ b/infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py @@ -47,6 +47,7 @@ def main(): GITHUB_WORKSPACE: The shared volume directory where input artifacts are. DRY_RUN: If true, no failures will surface. OSS_FUZZ_PROJECT_NAME: The name of the relevant OSS-Fuzz project. + SANITIZER: The sanitizer to use when running fuzzers. Returns: 0 on success or 1 on Failure. @@ -54,6 +55,8 @@ def main(): fuzz_seconds = int(os.environ.get('FUZZ_SECONDS', 600)) workspace = os.environ.get('GITHUB_WORKSPACE') oss_fuzz_project_name = os.environ.get('OSS_FUZZ_PROJECT_NAME') + sanitizer = os.environ.get('SANITIZER').lower() + # Check if failures should not be reported. dry_run = (os.environ.get('DRY_RUN').lower() == 'true') @@ -74,8 +77,10 @@ def main(): logging.error('This script needs to be run in the Github action context.') return returncode # Run the specified project's fuzzers from the build. - run_status, bug_found = cifuzz.run_fuzzers(fuzz_seconds, workspace, - oss_fuzz_project_name) + run_status, bug_found = cifuzz.run_fuzzers(fuzz_seconds, + workspace, + oss_fuzz_project_name, + sanitizer=sanitizer) if not run_status: logging.error('Error occured while running in workspace %s.', workspace) return returncode diff --git a/infra/cifuzz/cifuzz.py b/infra/cifuzz/cifuzz.py index b10660da..660075bf 100644 --- a/infra/cifuzz/cifuzz.py +++ b/infra/cifuzz/cifuzz.py @@ -68,7 +68,6 @@ STACKTRACE_END_MARKERS = [ # Default fuzz configuration. DEFAULT_ENGINE = 'libfuzzer' -DEFAULT_SANITIZER = 'address' DEFAULT_ARCHITECTURE = 'x86_64' # The path to get project's latest report json files. @@ -80,11 +79,14 @@ logging.basicConfig( level=logging.DEBUG) +# pylint: disable=too-many-arguments +# pylint: disable=too-many-locals def build_fuzzers(project_name, project_repo_name, workspace, pr_ref=None, - commit_sha=None): + commit_sha=None, + sanitizer='address'): """Builds all of the fuzzers for a specific OSS-Fuzz project. Args: @@ -94,6 +96,7 @@ def build_fuzzers(project_name, artifacts. pr_ref: The pull request reference to be built. commit_sha: The commit sha for the project to be built at. + sanitizer: The sanitizer the fuzzers should be built with. Returns: True if build succeeded or False on failure. @@ -104,6 +107,13 @@ def build_fuzzers(project_name, logging.error('Invalid workspace: %s.', workspace) return False + # Check that sanitizer is valid. + if not is_project_sanitizer(sanitizer, project_name): + logging.info("%s is not a project sanitizer, defaulting to address.", + sanitizer) + sanitizer = 'address' + logging.info("Using %s as sanitizer.", sanitizer) + git_workspace = os.path.join(workspace, 'storage') os.makedirs(git_workspace, exist_ok=True) out_dir = os.path.join(workspace, 'out') @@ -127,18 +137,14 @@ def build_fuzzers(project_name, build_repo_manager.checkout_pr(pr_ref) else: build_repo_manager.checkout_commit(commit_sha) - except RuntimeError: - logging.error('Can not check out requested state.') - return False - except ValueError: - logging.error('Invalid commit SHA requested %s.', commit_sha) - return False + except (RuntimeError, ValueError): + logging.error('Can not check out requested state %s.', pr_ref or commit_sha) + logging.error('Using current repo state.') # Build Fuzzers using docker run. command = [ '--cap-add', 'SYS_PTRACE', '-e', 'FUZZING_ENGINE=' + DEFAULT_ENGINE, '-e', - 'SANITIZER=' + DEFAULT_SANITIZER, '-e', - 'ARCHITECTURE=' + DEFAULT_ARCHITECTURE + 'SANITIZER=' + sanitizer, '-e', 'ARCHITECTURE=' + DEFAULT_ARCHITECTURE ] container = utils.get_container_name() if container: @@ -170,7 +176,7 @@ def build_fuzzers(project_name, return True -def run_fuzzers(fuzz_seconds, workspace, project_name): +def run_fuzzers(fuzz_seconds, workspace, project_name, sanitizer='address'): """Runs all fuzzers for a specific OSS-Fuzz project. Args: @@ -178,6 +184,7 @@ def run_fuzzers(fuzz_seconds, workspace, project_name): workspace: The location in a shared volume to store a git repo and build artifacts. project_name: The name of the relevant OSS-Fuzz project. + sanitizer: The sanitizer the fuzzers should be run with. Returns: (True if run was successful, True if bug was found). @@ -186,6 +193,14 @@ def run_fuzzers(fuzz_seconds, workspace, project_name): if not os.path.exists(workspace): logging.error('Invalid workspace: %s.', workspace) return False, False + + # Check that sanitizer is valid. + if not is_project_sanitizer(sanitizer, project_name): + logging.info("%s is not a project sanitizer.", sanitizer) + raise ValueError("{0} is not a valid sanitizer for project {1}".format( + sanitizer, project_name)) + logging.info("Using %s as sanitizer.", sanitizer) + out_dir = os.path.join(workspace, 'out') artifacts_dir = os.path.join(out_dir, 'artifacts') os.makedirs(artifacts_dir, exist_ok=True) @@ -209,8 +224,11 @@ def run_fuzzers(fuzz_seconds, workspace, project_name): run_seconds = max(fuzz_seconds // fuzzers_left_to_run, min_seconds_per_fuzzer) - target = fuzz_target.FuzzTarget(fuzzer_path, run_seconds, out_dir, - project_name) + target = fuzz_target.FuzzTarget(fuzzer_path, + run_seconds, + out_dir, + project_name, + sanitizer=sanitizer) start_time = time.time() test_case, stack_trace = target.fuzz() fuzz_seconds -= (time.time() - start_time) @@ -227,11 +245,12 @@ def run_fuzzers(fuzz_seconds, workspace, project_name): return True, False -def check_fuzzer_build(out_dir): +def check_fuzzer_build(out_dir, sanitizer='address'): """Checks the integrity of the built fuzzers. Args: out_dir: The directory containing the fuzzer binaries. + sanitizer: The sanitizer the fuzzers are built with. Returns: True if fuzzers are correct. @@ -245,8 +264,7 @@ def check_fuzzer_build(out_dir): command = [ '--cap-add', 'SYS_PTRACE', '-e', 'FUZZING_ENGINE=' + DEFAULT_ENGINE, '-e', - 'SANITIZER=' + DEFAULT_SANITIZER, '-e', - 'ARCHITECTURE=' + DEFAULT_ARCHITECTURE + 'SANITIZER=' + sanitizer, '-e', 'ARCHITECTURE=' + DEFAULT_ARCHITECTURE ] container = utils.get_container_name() if container: @@ -460,3 +478,27 @@ def parse_fuzzer_output(fuzzer_output, out_dir): summary_file_path = os.path.join(out_dir, 'bug_summary.txt') with open(summary_file_path, 'a') as summary_handle: summary_handle.write(summary_str) + + +def is_project_sanitizer(sanitizer, oss_fuzz_project_name): + """Finds all of the sanitizers a project can use for building and running. + + Args: + sanitizer: The desired sanitizer. + oss_fuzz_project_name: The name of the relevant OSS-Fuzz project. + + Returns: + True if project can use sanitizer. + """ + project_yaml = os.path.join(helper.OSS_FUZZ_DIR, 'projects', + oss_fuzz_project_name, 'project.yaml') + if not os.path.isfile(project_yaml): + logging.error('project.yaml for project %s could not be found.', + oss_fuzz_project_name) + return False + + # Simple parse to prevent adding pyYAML dependency. + with open(project_yaml, 'r') as file_handle: + if sanitizer + '\n' in file_handle.read(): + return True + return False diff --git a/infra/cifuzz/cifuzz_test.py b/infra/cifuzz/cifuzz_test.py index 8a792fff..2da8b5ff 100644 --- a/infra/cifuzz/cifuzz_test.py +++ b/infra/cifuzz/cifuzz_test.py @@ -50,6 +50,12 @@ EXAMPLE_NOCRASH_FUZZER = 'example_nocrash_fuzzer' # A fuzzer to be built in build_fuzzers integration tests. EXAMPLE_BUILD_FUZZER = 'do_stuff_fuzzer' +MEMORY_FUZZER_DIR = os.path.join(TEST_FILES_PATH, 'out', 'memory') +MEMORY_FUZZER = 'curl_fuzzer_memory' + +UNDEFINED_FUZZER_DIR = os.path.join(TEST_FILES_PATH, 'out', 'undefined') +UNDEFINED_FUZZER = 'curl_fuzzer_undefined' + class BuildFuzzersIntegrationTest(unittest.TestCase): """Test build_fuzzers function in the utils module.""" @@ -86,7 +92,7 @@ class BuildFuzzersIntegrationTest(unittest.TestCase): with tempfile.TemporaryDirectory() as tmp_dir: out_path = os.path.join(tmp_dir, 'out') os.mkdir(out_path) - self.assertFalse( + self.assertTrue( cifuzz.build_fuzzers(EXAMPLE_PROJECT, 'oss-fuzz', tmp_dir, @@ -132,16 +138,70 @@ class BuildFuzzersIntegrationTest(unittest.TestCase): )) -class RunFuzzersIntegrationTest(unittest.TestCase): +class RunMemoryFuzzerIntegrationTest(unittest.TestCase): """Test build_fuzzers function in the cifuzz module.""" def tearDown(self): """Remove any existing crashes and test files.""" - out_dir = os.path.join(TEST_FILES_PATH, 'out') + out_dir = os.path.join(MEMORY_FUZZER_DIR, 'out') + for out_file in os.listdir(out_dir): + out_path = os.path.join(out_dir, out_file) + #pylint: disable=consider-using-in + if out_file == MEMORY_FUZZER: + continue + if os.path.isdir(out_path): + shutil.rmtree(out_path) + else: + os.remove(out_path) + + def test_run_with_memory_sanitizer(self): + """Test run_fuzzers with a valid build.""" + run_success, bug_found = cifuzz.run_fuzzers(10, + MEMORY_FUZZER_DIR, + 'curl', + sanitizer='memory') + self.assertTrue(run_success) + self.assertFalse(bug_found) + + +class RunUndefinedFuzzerIntegrationTest(unittest.TestCase): + """Test build_fuzzers function in the cifuzz module.""" + + def tearDown(self): + """Remove any existing crashes and test files.""" + out_dir = os.path.join(UNDEFINED_FUZZER_DIR, 'out') for out_file in os.listdir(out_dir): out_path = os.path.join(out_dir, out_file) #pylint: disable=consider-using-in - if out_file == EXAMPLE_CRASH_FUZZER or out_file == EXAMPLE_NOCRASH_FUZZER: + if out_file == UNDEFINED_FUZZER: + continue + if os.path.isdir(out_path): + shutil.rmtree(out_path) + else: + os.remove(out_path) + + def test_run_with_undefined_sanitizer(self): + """Test run_fuzzers with a valid build.""" + run_success, bug_found = cifuzz.run_fuzzers(10, + UNDEFINED_FUZZER_DIR, + 'curl', + sanitizer='undefined') + self.assertTrue(run_success) + self.assertFalse(bug_found) + + +class RunAddressFuzzersIntegrationTest(unittest.TestCase): + """Test build_fuzzers function in the cifuzz module.""" + + def tearDown(self): + """Remove any existing crashes and test files.""" + out_dir = os.path.join(TEST_FILES_PATH, 'out') + files_to_keep = [ + 'undefined', 'memory', EXAMPLE_CRASH_FUZZER, EXAMPLE_NOCRASH_FUZZER + ] + for out_file in os.listdir(out_dir): + out_path = os.path.join(out_dir, out_file) + if out_file in files_to_keep: continue if os.path.isdir(out_path): shutil.rmtree(out_path) @@ -153,9 +213,12 @@ class RunFuzzersIntegrationTest(unittest.TestCase): # Set the first return value to True, then the second to False to # emulate a bug existing in the current PR but not on the downloaded # OSS-Fuzz build. - with unittest.mock.patch.object(fuzz_target.FuzzTarget, - 'is_reproducible', - side_effect=[True, False]): + with unittest.mock.patch.object( + fuzz_target.FuzzTarget, 'is_reproducible', + side_effect=[True, + False]), unittest.mock.patch.object(cifuzz, + 'is_project_sanitizer', + return_value=True): run_success, bug_found = cifuzz.run_fuzzers(10, TEST_FILES_PATH, EXAMPLE_PROJECT) build_dir = os.path.join(TEST_FILES_PATH, 'out', 'oss_fuzz_latest') @@ -166,9 +229,12 @@ class RunFuzzersIntegrationTest(unittest.TestCase): def test_old_bug_found(self): """Test run_fuzzers with a bug found in OSS-Fuzz before.""" - with unittest.mock.patch.object(fuzz_target.FuzzTarget, - 'is_reproducible', - side_effect=[True, True]): + with unittest.mock.patch.object( + fuzz_target.FuzzTarget, 'is_reproducible', + side_effect=[True, + True]), unittest.mock.patch.object(cifuzz, + 'is_project_sanitizer', + return_value=True): run_success, bug_found = cifuzz.run_fuzzers(10, TEST_FILES_PATH, EXAMPLE_PROJECT) build_dir = os.path.join(TEST_FILES_PATH, 'out', 'oss_fuzz_latest') @@ -179,7 +245,8 @@ class RunFuzzersIntegrationTest(unittest.TestCase): def test_invalid_build(self): """Test run_fuzzers with an invalid build.""" - with tempfile.TemporaryDirectory() as tmp_dir: + with tempfile.TemporaryDirectory() as tmp_dir, unittest.mock.patch.object( + cifuzz, 'is_project_sanitizer', return_value=True): out_path = os.path.join(tmp_dir, 'out') os.mkdir(out_path) run_success, bug_found = cifuzz.run_fuzzers(10, tmp_dir, EXAMPLE_PROJECT) @@ -188,7 +255,8 @@ class RunFuzzersIntegrationTest(unittest.TestCase): def test_invalid_fuzz_seconds(self): """Tests run_fuzzers with an invalid fuzz seconds.""" - with tempfile.TemporaryDirectory() as tmp_dir: + with tempfile.TemporaryDirectory() as tmp_dir, unittest.mock.patch.object( + cifuzz, 'is_project_sanitizer', return_value=True): out_path = os.path.join(tmp_dir, 'out') os.mkdir(out_path) run_success, bug_found = cifuzz.run_fuzzers(0, tmp_dir, EXAMPLE_PROJECT) @@ -424,5 +492,57 @@ class KeepAffectedFuzzersUnitTest(unittest.TestCase): self.assertEqual(2, len(os.listdir(tmp_dir))) +class IsProjectSanitizerUnitTest(unittest.TestCase): + """Class to test the is_project_sanitizer function in the cifuzz module. + Note: This test relies on the curl project being an OSS-Fuzz project. + """ + + def test_valid_project_curl(self): + """Test if sanitizers can be detected from project.yaml""" + self.assertTrue(cifuzz.is_project_sanitizer('memory', 'curl')) + self.assertTrue(cifuzz.is_project_sanitizer('address', 'curl')) + self.assertTrue(cifuzz.is_project_sanitizer('undefined', 'curl')) + self.assertFalse(cifuzz.is_project_sanitizer('not-a-san', 'curl')) + + def test_valid_project_example(self): + """Test if sanitizers can be detected from project.yaml""" + self.assertFalse(cifuzz.is_project_sanitizer('memory', 'example')) + self.assertFalse(cifuzz.is_project_sanitizer('address', 'example')) + self.assertFalse(cifuzz.is_project_sanitizer('undefined', 'example')) + self.assertFalse(cifuzz.is_project_sanitizer('not-a-san', 'example')) + + def test_invalid_project(self): + """Tests that invalid projects return false.""" + self.assertFalse(cifuzz.is_project_sanitizer('memory', 'notaproj')) + self.assertFalse(cifuzz.is_project_sanitizer('address', 'notaproj')) + self.assertFalse(cifuzz.is_project_sanitizer('undefined', 'notaproj')) + + +@unittest.skip('Test is too long to be run with presubmit.') +class BuildSantizerIntegrationTest(unittest.TestCase): + """Class to test the is_project_sanitizer function in the cifuzz module. + Note: This test relies on the curl project being an OSS-Fuzz project.""" + + def test_valid_project_curl_memory(self): + """Test if sanitizers can be detected from project.yaml""" + with tempfile.TemporaryDirectory() as tmp_dir: + self.assertTrue( + cifuzz.build_fuzzers('curl', + 'curl', + tmp_dir, + pr_ref='fake_pr', + sanitizer='memory')) + + def test_valid_project_curl_undefined(self): + """Test if sanitizers can be detected from project.yaml""" + with tempfile.TemporaryDirectory() as tmp_dir: + self.assertTrue( + cifuzz.build_fuzzers('curl', + 'curl', + tmp_dir, + pr_ref='fake_pr', + sanitizer='undefined')) + + if __name__ == '__main__': unittest.main() diff --git a/infra/cifuzz/fuzz_target.py b/infra/cifuzz/fuzz_target.py index 85fdbdc9..60b15145 100644 --- a/infra/cifuzz/fuzz_target.py +++ b/infra/cifuzz/fuzz_target.py @@ -51,9 +51,6 @@ BUILD_ARCHIVE_NAME = 'oss_fuzz_latest.zip' # Zip file name containing the corpus. CORPUS_ZIP_NAME = 'public.zip' -# The sanitizer build to download. -SANITIZER = 'address' - # The number of reproduce attempts for a crash. REPRODUCE_ATTEMPTS = 10 @@ -72,7 +69,13 @@ class FuzzTarget: project_name: The name of the relevant OSS-Fuzz project. """ - def __init__(self, target_path, duration, out_dir, project_name=None): + #pylint: disable=too-many-arguments + def __init__(self, + target_path, + duration, + out_dir, + project_name=None, + sanitizer='address'): """Represents a single fuzz target. Note: project_name should be none when the fuzzer being run is not @@ -89,6 +92,7 @@ class FuzzTarget: self.target_path = target_path self.out_dir = out_dir self.project_name = project_name + self.sanitizer = sanitizer def fuzz(self): """Starts the fuzz target run for the length of time specified by duration. @@ -108,8 +112,8 @@ class FuzzTarget: command += ['-v', '%s:%s' % (self.out_dir, '/out')] command += [ - '-e', 'FUZZING_ENGINE=libfuzzer', '-e', 'SANITIZER=address', '-e', - 'RUN_FUZZER_MODE=interactive', 'gcr.io/oss-fuzz-base/base-runner', + '-e', 'FUZZING_ENGINE=libfuzzer', '-e', 'SANITIZER=' + self.sanitizer, + '-e', 'RUN_FUZZER_MODE=interactive', 'gcr.io/oss-fuzz-base/base-runner', 'bash', '-c' ] @@ -253,7 +257,7 @@ class FuzzTarget: return None version = VERSION_STRING.format(project_name=self.project_name, - sanitizer=SANITIZER) + sanitizer=self.sanitizer) version_url = url_join(GCS_BASE_URL, CLUSTERFUZZ_BUILDS, self.project_name, version) try: diff --git a/infra/cifuzz/test_files/out/memory/out/curl_fuzzer_memory b/infra/cifuzz/test_files/out/memory/out/curl_fuzzer_memory Binary files differnew file mode 100755 index 00000000..c602ce97 --- /dev/null +++ b/infra/cifuzz/test_files/out/memory/out/curl_fuzzer_memory diff --git a/infra/cifuzz/test_files/out/undefined/out/curl_fuzzer_undefined b/infra/cifuzz/test_files/out/undefined/out/curl_fuzzer_undefined Binary files differnew file mode 100755 index 00000000..504cab10 --- /dev/null +++ b/infra/cifuzz/test_files/out/undefined/out/curl_fuzzer_undefined |