aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--infra/bisector.py42
-rw-r--r--infra/build_specified_commit.py55
-rw-r--r--infra/repo_manager.py2
3 files changed, 92 insertions, 7 deletions
diff --git a/infra/bisector.py b/infra/bisector.py
index 39c35479c..1e762fa06 100644
--- a/infra/bisector.py
+++ b/infra/bisector.py
@@ -32,6 +32,9 @@ This is done with the following steps:
import argparse
import collections
+import datetime
+from distutils import spawn
+import json
import logging
import os
import tempfile
@@ -94,8 +97,33 @@ def main():
return 0
+def _load_base_builder_repo():
+ """Get base-image digests."""
+ gcloud_path = spawn.find_executable('gcloud')
+ if not gcloud_path:
+ logging.warning('gcloud not found in PATH.')
+ return None
+
+ result, _, _ = utils.execute([
+ gcloud_path, 'container', 'images', 'list-tags',
+ 'gcr.io/oss-fuzz-base/base-builder', '--format=json'
+ ],
+ check_result=True)
+ result = json.loads(result)
+
+ repo = build_specified_commit.BaseBuilderRepo()
+ for image in result:
+ timestamp = datetime.datetime.fromisoformat(
+ image['timestamp']['datetime']).astimezone(datetime.timezone.utc)
+ repo.add_digest(timestamp, image['digest'])
+
+ return repo
+
+
def _bisect(old_commit, new_commit, test_case_path, fuzz_target, build_data): # pylint: disable=too-many-locals
"""Perform the bisect."""
+ base_builder_repo = _load_base_builder_repo()
+
with tempfile.TemporaryDirectory() as tmp_dir:
repo_url, repo_path = build_specified_commit.detect_main_repo(
build_data.project_name, commit=new_commit)
@@ -115,7 +143,11 @@ def _bisect(old_commit, new_commit, test_case_path, fuzz_target, build_data): #
new_idx = 0
logging.info('Testing against new_commit (%s)', commit_list[new_idx])
if not build_specified_commit.build_fuzzers_from_commit(
- commit_list[new_idx], bisect_repo_manager, host_src_dir, build_data):
+ commit_list[new_idx],
+ bisect_repo_manager,
+ host_src_dir,
+ build_data,
+ base_builder_repo=base_builder_repo):
raise RuntimeError('Failed to build new_commit')
expected_error_code = helper.reproduce_impl(build_data.project_name,
@@ -130,7 +162,7 @@ def _bisect(old_commit, new_commit, test_case_path, fuzz_target, build_data): #
bisect_repo_manager,
host_src_dir,
build_data,
- ):
+ base_builder_repo=base_builder_repo):
raise RuntimeError('Failed to build old_commit')
if expected_error_code == helper.reproduce_impl(build_data.project_name,
@@ -146,7 +178,11 @@ def _bisect(old_commit, new_commit, test_case_path, fuzz_target, build_data): #
logging.info('Testing against %s (idx=%d)', commit_list[curr_idx],
curr_idx)
if not build_specified_commit.build_fuzzers_from_commit(
- commit_list[curr_idx], bisect_repo_manager, host_src_dir, build_data):
+ commit_list[curr_idx],
+ bisect_repo_manager,
+ host_src_dir,
+ build_data,
+ base_builder_repo=base_builder_repo):
# Treat build failures as if we couldn't repo.
# TODO(ochang): retry nearby commits?
old_idx = curr_idx
diff --git a/infra/build_specified_commit.py b/infra/build_specified_commit.py
index b0f7b5b29..0a7a5259c 100644
--- a/infra/build_specified_commit.py
+++ b/infra/build_specified_commit.py
@@ -17,6 +17,7 @@ This module is allows each of the OSS Fuzz projects fuzzers to be built
from a specific point in time. This feature can be used for implementations
like continuious integration fuzzing and bisection to find errors
"""
+import bisect
import os
import collections
import logging
@@ -33,6 +34,27 @@ BuildData = collections.namedtuple(
_GIT_DIR_MARKER = 'gitdir: '
+class BaseBuilderRepo:
+ """Repo of base-builder images."""
+
+ def __init__(self):
+ self.timestamps = []
+ self.digests = []
+
+ def add_digest(self, timestamp, digest):
+ """Add a digest."""
+ self.timestamps.append(timestamp)
+ self.digests.append(digest)
+
+ def find_digest(self, timestamp):
+ """Find the latest image before the given timestamp."""
+ index = bisect.bisect_right(self.timestamps, timestamp)
+ if index > 0:
+ return self.digests[index - 1]
+
+ raise ValueError('Failed to find suitable base-builder.')
+
+
def _make_gitdirs_relative(src_dir):
"""Make gitdirs relative."""
for root_dir, _, files in os.walk(src_dir):
@@ -60,7 +82,23 @@ def _make_gitdirs_relative(src_dir):
new_lines.append(line)
with open(file_path, 'w') as handle:
- handle.write('\n'.join(new_lines))
+ handle.write(''.join(new_lines))
+
+
+def _replace_base_builder_digest(dockerfile_path, digest):
+ """Replace the base-builder digest in a Dockerfile."""
+ with open(dockerfile_path) as handle:
+ lines = handle.readlines()
+
+ new_lines = []
+ for line in lines:
+ if line.strip().startswith('FROM'):
+ line = 'FROM gcr.io/oss-fuzz-base/base-builder@' + digest
+
+ new_lines.append(line)
+
+ with open(dockerfile_path, 'w') as handle:
+ handle.write(''.join(new_lines))
def copy_src_from_docker(project_name, host_dir):
@@ -87,14 +125,18 @@ def copy_src_from_docker(project_name, host_dir):
return src_dir
-def build_fuzzers_from_commit(commit, build_repo_manager, host_src_path,
- build_data):
+def build_fuzzers_from_commit(commit,
+ build_repo_manager,
+ host_src_path,
+ build_data,
+ base_builder_repo=None):
"""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.
+ base_builder_repo: A BaseBuilderRepo.
Returns:
0 on successful build or error code on failure.
"""
@@ -138,6 +180,13 @@ def build_fuzzers_from_commit(commit, build_repo_manager, host_src_path,
oss_fuzz_repo_manager.git(['checkout', oss_fuzz_commit, projects_dir],
check_result=True)
+ # Also use the closest base-builder we can find.
+ if base_builder_repo:
+ base_builder_digest = base_builder_repo.find_digest(commit_date)
+ logging.info('Using base-builder with digest %s.', base_builder_digest)
+ _replace_base_builder_digest(os.path.join(projects_dir, 'Dockerfile'),
+ base_builder_digest)
+
# 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.')
diff --git a/infra/repo_manager.py b/infra/repo_manager.py
index 238cb3e7b..946559849 100644
--- a/infra/repo_manager.py
+++ b/infra/repo_manager.py
@@ -84,7 +84,7 @@ class BaseRepoManager:
"""
out, _, _ = self.git(['show', '-s', '--format=%ct', commit],
check_result=True)
- return datetime.datetime.fromtimestamp(int(out))
+ return datetime.datetime.fromtimestamp(int(out), tz=datetime.timezone.utc)
def get_git_diff(self):
"""Gets a list of files that have changed from the repo head.