diff options
author | Jonathan Metzman <metzman@chromium.org> | 2021-01-20 07:25:08 -0800 |
---|---|---|
committer | Jonathan Metzman <metzman@chromium.org> | 2021-01-20 07:25:08 -0800 |
commit | ddb0add036e34a85d6ab4a711d7c29d7fe15ac19 (patch) | |
tree | f8445c6a2875b8cf5e08891cef507a80b6492bf4 /infra/cifuzz/affected_fuzz_targets.py | |
parent | 0be9a235b81a82c2bafd84a645ada249b6f8aefb (diff) |
fuzzers->fuzz targets and finish affected_fuzz_targets module
Diffstat (limited to 'infra/cifuzz/affected_fuzz_targets.py')
-rw-r--r-- | infra/cifuzz/affected_fuzz_targets.py | 100 |
1 files changed, 91 insertions, 9 deletions
diff --git a/infra/cifuzz/affected_fuzz_targets.py b/infra/cifuzz/affected_fuzz_targets.py index ca81752b..b5f51ad6 100644 --- a/infra/cifuzz/affected_fuzz_targets.py +++ b/infra/cifuzz/affected_fuzz_targets.py @@ -11,7 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Module for dealing with fuzzers affected by the change-under-test (CUT).""" +"""Module for dealing with fuzz targets affected by the change-under-test +(CUT).""" +import logging import os import sys @@ -21,11 +23,91 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import utils -def fix_git_repo_for_diff(repo_dir): - """Fixes git repos cloned by the "checkout" action so that diffing works on - them.""" - command = [ - 'git', 'symbolic-ref', 'refs/remotes/origin/HEAD', - 'refs/remotes/origin/master' - ] - return utils.execute(command, location=repo_dir) +def remove_unaffected_fuzz_targets(project_name, out_dir, files_changed, + repo_path): + """Removes all non affected fuzz targets in the out directory. + + Args: + project_name: The name of the relevant OSS-Fuzz project. + out_dir: The location of the fuzz target binaries. + files_changed: A list of files changed compared to HEAD. + repo_path: The location of the OSS-Fuzz repo in the docker image. + + This function will not delete fuzz targets unless it knows that the fuzz + targets are unaffected. For example, this means that fuzz targets which don't + have coverage data on will not be deleted. + """ + if not files_changed: + # Don't remove any fuzz targets if there is no difference from HEAD. + logging.info('No files changed.') + return + + logging.info('Files changed in PR: %s', files_changed) + + fuzz_target_paths = utils.get_fuzz_targets(out_dir) + if not fuzz_target_paths: + # Nothing to remove. + logging.error('No fuzz targets found in out dir.') + return + + coverage_getter = coverage.OSSFuzzCoveragGetter(project_name, + repo_path) + if coverage_getter.fuzzer_stats_url: + # Don't remove any fuzz targets unless we have data. + logging.error('Could not download latest coverage report.') + return + + affected_fuzz_targets = get_affected_fuzz_targets( + coverage_getter, fuzz_target_paths, files_changed) + + if not affected_fuzz_targets: + logging.info('No affected fuzz targets detected, keeping all as fallback.') + return + + logging.info('Using affected fuzz targets: %s.', affected_fuzz_targets) + unaffected_fuzz_targets = set(fuzz_target_paths) - affected_fuzz_targets + logging.info('Removing unaffected fuzz targets: %s.', unaffected_fuzz_targets) + + # Remove all the targets that are not affected. + for fuzz_target_path in unaffected_fuzz_targets: + try: + os.remove(fuzz_target_path) + except OSError as error: + logging.error('%s occurred while removing file %s', + error, fuzz_target_path) + + +def is_fuzz_target_affected(coverage_getter, fuzz_target_path, files_changed): + """Returns True if a fuzz target (|fuzz_target_path| is affected by + |files_changed|.""" + fuzz_target = os.path.basename(fuzz_target_path) + covered_files = coverage_getter.get_files_covered_by_target(fuzz_target) + if not covered_files: + # Assume a fuzz target is affected if we can't get its coverage from + # OSS-Fuzz. + logging.info('Could not get coverage for %s. Treating as affected.', + fuzz_target) + return True + + logging.info('Fuzz target %s is affected by: %s', + fuzz_target, covered_files) + for filename in files_changed: + if filename in covered_files: + logging.info('Fuzz target %s is affected by changed file: %s', + fuzz_target, filename) + return True + + logging.info('Fuzz target %s is not affected.', fuzz_target) + return False + + +def get_affected_fuzz_targets( + coverage_getter, fuzz_target_paths, files_changed): + """Returns a list of paths of affected targets.""" + affected_fuzz_targets = set() + for fuzz_target_path in fuzz_target_paths: + if is_fuzz_target_affected( + coverage_getter, fuzz_target_path, files_changed): + affected_fuzz_targets.add(fuzz_target_path) + + return affected_fuzz_targets |