diff options
-rw-r--r-- | .pylintrc | 2 | ||||
-rw-r--r-- | infra/base-images/base-builder/bisect_clang.py | 29 | ||||
-rw-r--r-- | infra/base-images/base-builder/bisect_clang_test.py | 15 | ||||
-rwxr-xr-x | infra/base-images/base-builder/write_labels.py | 7 | ||||
-rwxr-xr-x | infra/base-images/base-runner/dataflow_tracer.py | 59 | ||||
-rw-r--r-- | infra/bisector_test.py | 12 | ||||
-rwxr-xr-x | infra/presubmit.py | 17 |
7 files changed, 75 insertions, 66 deletions
@@ -7,7 +7,7 @@ extension-pkg-whitelist= # Add files or directories to the blacklist. They should be base names, not # paths. -ignore=CVS +ignore=CVS,base-clang,base-sanitizer-libs-builder # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. diff --git a/infra/base-images/base-builder/bisect_clang.py b/infra/base-images/base-builder/bisect_clang.py index 2e7db61ca..8c530b627 100644 --- a/infra/base-images/base-builder/bisect_clang.py +++ b/infra/base-images/base-builder/bisect_clang.py @@ -112,7 +112,7 @@ class GitRepo: class BisectError(Exception): - pass + """Error that was encountered during bisection.""" def get_clang_build_env(): @@ -140,8 +140,8 @@ def clone_with_retries(repo, local_path, num_retries=10): for _ in range(num_retries): if os.path.isdir(local_path): shutil.rmtree(local_path) - retcode, _, _ = execute( - ['git', 'clone', repo, local_path], expect_zero=False) + retcode, _, _ = execute(['git', 'clone', repo, local_path], + expect_zero=False) if retcode == 0: return raise Exception('Could not checkout %s.' % repo) @@ -162,17 +162,16 @@ def prepare_build(llvm_project_path): llvm_build_dir = os.path.join(os.getenv('WORK'), 'llvm-build') if not os.path.exists(llvm_build_dir): os.mkdir(llvm_build_dir) - execute( - [ - 'cmake', '-G', 'Ninja', '-DLIBCXX_ENABLE_SHARED=OFF', - '-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON', - '-DLIBCXXABI_ENABLE_SHARED=OFF', '-DCMAKE_BUILD_TYPE=Release', - '-DLLVM_ENABLE_PROJECTS=libcxx;libcxxabi;compiler-rt;clang', - '-DLLVM_TARGETS_TO_BUILD=' + get_clang_target_arch(), - os.path.join(llvm_project_path, 'llvm') - ], - env=get_clang_build_env(), - cwd=llvm_build_dir) + execute([ + 'cmake', '-G', 'Ninja', '-DLIBCXX_ENABLE_SHARED=OFF', + '-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON', '-DLIBCXXABI_ENABLE_SHARED=OFF', + '-DCMAKE_BUILD_TYPE=Release', + '-DLLVM_ENABLE_PROJECTS=libcxx;libcxxabi;compiler-rt;clang', + '-DLLVM_TARGETS_TO_BUILD=' + get_clang_target_arch(), + os.path.join(llvm_project_path, 'llvm') + ], + env=get_clang_build_env(), + cwd=llvm_build_dir) return llvm_build_dir @@ -201,12 +200,14 @@ def find_culprit_commit(test_command, good_commit, bad_commit): def main(): + # pylint: disable=line-too-long """Finds the culprit LLVM commit that introduced a clang regression. Can be tested using this command in a libsodium shell: python3 bisect_clang.py "cd /src/libsodium; make clean; cd -; compile && /out/secret_key_auth_fuzzer -runs=100" \ f7e52fbdb5a7af8ea0808e98458b497125a5eca1 \ 8288453f6aac05080b751b680455349e09d49825 """ + # pylint: enable=line-too-long # TODO(metzman): Sanity check CFLAGS for things like # -fsanitize=fuzzer-no-link. # TODO(metzman): Allow test_command to be optional and for just build.sh to be diff --git a/infra/base-images/base-builder/bisect_clang_test.py b/infra/base-images/base-builder/bisect_clang_test.py index 7a11021d1..edf13e759 100644 --- a/infra/base-images/base-builder/bisect_clang_test.py +++ b/infra/base-images/base-builder/bisect_clang_test.py @@ -37,10 +37,11 @@ def patch_environ(testcase_obj): patcher.start() -class BisectClangTestMixin: +class BisectClangTestMixin: # pylint: disable=too-few-public-methods """Useful mixin for bisect_clang unittests.""" - def setUp(self): + def setUp(self): # pylint: disable=invalid-name + """Initialization method for unittests.""" patch_environ(self) os.environ['SRC'] = '/src' os.environ['WORK'] = '/work' @@ -68,8 +69,9 @@ class GetClangBuildEnvTest(BisectClangTestMixin, unittest.TestCase): def read_test_data(filename): - with open(os.path.join(FILE_DIRECTORY, 'test_data', filename)) as f: - return f.read() + """Returns data from |filename| in the test_data directory.""" + with open(os.path.join(FILE_DIRECTORY, 'test_data', filename)) as file_handle: + return file_handle.read() class SearchBisectOutputTest(BisectClangTestMixin, unittest.TestCase): @@ -126,6 +128,7 @@ def create_mock_popen( def mock_prepare_build(llvm_project_path): # pylint: disable=unused-argument + """Mocked prepare_build function.""" return '/work/llvm-build' @@ -224,8 +227,8 @@ class GitRepoTest(BisectClangTestMixin, unittest.TestCase): with mock.patch('subprocess.Popen', create_mock_popen()) as mock_popen: self.git.bisect_start(self.good_commit, self.bad_commit, self.test_command) - self.assertEqual( - get_git_command('bisect', 'start'), mock_popen.commands[0]) + self.assertEqual(get_git_command('bisect', 'start'), + mock_popen.commands[0]) mock_test_start_commit.assert_has_calls([ mock.call('bad_commit', 'bad', 'testcommand'), mock.call('good_commit', 'good', 'testcommand') diff --git a/infra/base-images/base-builder/write_labels.py b/infra/base-images/base-builder/write_labels.py index 338ac716f..b1a748427 100755 --- a/infra/base-images/base-builder/write_labels.py +++ b/infra/base-images/base-builder/write_labels.py @@ -1,10 +1,13 @@ #!/usr/bin/python3 +"""Script for writing from project.yaml to .labels file.""" import os import json import sys + def main(): + """Writes labels.""" if len(sys.argv) != 3: print('Usage: write_labels.py labels_json out_dir', file=sys.stderr) sys.exit(1) @@ -13,8 +16,8 @@ def main(): out = sys.argv[2] for target_name, labels in labels_by_target.items(): - with open(os.path.join(out, target_name + '.labels'), 'w') as f: - f.write('\n'.join(labels)) + with open(os.path.join(out, target_name + '.labels'), 'w') as file_handle: + file_handle.write('\n'.join(labels)) if __name__ == '__main__': diff --git a/infra/base-images/base-runner/dataflow_tracer.py b/infra/base-images/base-runner/dataflow_tracer.py index 7166bf43e..66855a0f7 100755 --- a/infra/base-images/base-runner/dataflow_tracer.py +++ b/infra/base-images/base-runner/dataflow_tracer.py @@ -26,9 +26,9 @@ import sys # These can be controlled by the runner in order to change the values without # rebuilding OSS-Fuzz base images. -FILE_SIZE_LIMIT = int(os.getenv('DFT_FILE_SIZE_LIMIT', 32 * 1024)) -MIN_TIMEOUT = float(os.getenv('DFT_MIN_TIMEOUT', 1.0)) -TIMEOUT_RANGE = float(os.getenv('DFT_TIMEOUT_RANGE', 3.0)) +FILE_SIZE_LIMIT = int(os.environ.get('DFT_FILE_SIZE_LIMIT', 32 * 1024)) +MIN_TIMEOUT = float(os.environ.get('DFT_MIN_TIMEOUT', 1.0)) +TIMEOUT_RANGE = float(os.enviro.get('DFT_TIMEOUT_RANGE', 3.0)) DFSAN_OPTIONS = 'fast16labels=1:warn_unimplemented=0' @@ -39,15 +39,15 @@ def _error(msg): def _list_dir(dirpath): for root, _, files in os.walk(dirpath): - for f in files: - yield os.path.join(root, f) + for filename in files: + yield os.path.join(root, filename) def _sha1(filepath): - h = hashlib.sha1() - with open(filepath, 'rb') as f: - h.update(f.read()) - return h.hexdigest() + digest = hashlib.sha1() + with open(filepath, 'rb') as file_handle: + digest.update(file_handle.read()) + return digest.hexdigest() def _run(cmd, timeout=None): @@ -56,15 +56,16 @@ def _run(cmd, timeout=None): result = subprocess.run(cmd, timeout=timeout, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stderr=subprocess.PIPE, + check=False) if result.returncode: _error('{command} finished with non-zero code: {code}'.format( command=str(cmd), code=result.returncode)) except subprocess.TimeoutExpired: raise - except Exception as e: - _error('Exception: ' + str(e)) + except Exception as error: # pylint: disable=broad-except + _error('Exception: ' + str(error)) return result @@ -77,6 +78,7 @@ def _timeout(size): def collect_traces(binary, corpus_dir, dft_dir): + """Collects traces and returns stats summarizing them.""" stats = { 'total': 0, 'traced': 0, @@ -86,43 +88,46 @@ def collect_traces(binary, corpus_dir, dft_dir): } files_and_sizes = {} - for f in _list_dir(corpus_dir): + for filename in _list_dir(corpus_dir): stats['total'] += 1 - size = os.path.getsize(f) + size = os.path.getsize(filename) if size > FILE_SIZE_LIMIT: stats['long'] += 1 - print('Skipping large file ({size}b): {path}'.format(size=size, path=f)) + print('Skipping large file ({size}b): {path}'.format(size=size, + path=filename)) continue - files_and_sizes[f] = size + files_and_sizes[filename] = size - for f in sorted(files_and_sizes, key=files_and_sizes.get): - output_path = os.path.join(dft_dir, _sha1(f)) + for filename in sorted(files_and_sizes, key=files_and_sizes.get): + output_path = os.path.join(dft_dir, _sha1(filename)) try: - result = _run([binary, f, output_path], timeout=_timeout(size)) + result = _run([binary, filename, output_path], timeout=_timeout(size)) if result.returncode: stats['failed'] += 1 else: stats['traced'] += 1 - except subprocess.TimeoutExpired as e: - _error('Slow input: ' + str(e)) + except subprocess.TimeoutExpired as error: + _error('Slow input: ' + str(error)) stats['slow'] += 1 return stats def dump_functions(binary, dft_dir): + """Dumps functions to functions.txt. Returns True on success.""" result = _run([binary]) if not result or result.returncode: return False - with open(os.path.join(dft_dir, 'functions.txt'), 'wb') as f: - f.write(result.stdout) + with open(os.path.join(dft_dir, 'functions.txt'), 'wb') as file_handle: + file_handle.write(result.stdout) return True def main(): + """Collect dataflow traces.""" if len(sys.argv) < 4: _error('Usage: {0} <binary> <corpus_dir> <dft_dir>'.format(sys.argv[0])) sys.exit(1) @@ -138,13 +143,13 @@ def main(): sys.exit(1) stats = collect_traces(binary, corpus_dir, dft_dir) - for k, v in stats.items(): - print('{0}: {1}'.format(k, v)) + for key, value in stats.items(): + print('{0}: {1}'.format(key, value)) # Checksum that we didn't lose track of any of the inputs. - assert stats['total'] * 2 == sum(v for v in stats.values()) + assert stats['total'] * 2 == sum(value for value in stats.values()) sys.exit(0) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/infra/bisector_test.py b/infra/bisector_test.py index 883e0f1fc..5e3dc5232 100644 --- a/infra/bisector_test.py +++ b/infra/bisector_test.py @@ -33,6 +33,8 @@ TEST_DIR_PATH = os.path.dirname(os.path.realpath(__file__)) class BisectIntegrationTests(unittest.TestCase): """Class to test the functionality of bisection method.""" + BISECT_TYPE = 'regressed' + def test_bisect_invalid_repo(self): """Test the bisection method on a project that does not exist.""" test_repo = test_repos.INVALID_REPO @@ -42,9 +44,9 @@ class BisectIntegrationTests(unittest.TestCase): 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) + bisector.bisect(self.BISECT_TYPE, test_repo.old_commit, + test_repo.new_commit, test_repo.test_case_path, + test_repo.fuzz_target, build_data) def test_bisect(self): """Test the bisect method on example projects.""" @@ -55,8 +57,8 @@ class BisectIntegrationTests(unittest.TestCase): engine='libfuzzer', sanitizer='address', architecture='x86_64') - result = bisector.bisect(test_repo.old_commit, test_repo.new_commit, - test_repo.test_case_path, + result = bisector.bisect(self.BISECT_TYPE, test_repo.old_commit, + test_repo.new_commit, test_repo.test_case_path, test_repo.fuzz_target, build_data) self.assertEqual(result.commit, test_repo.intro_commit) diff --git a/infra/presubmit.py b/infra/presubmit.py index 3a616aca0..7699ab9ff 100755 --- a/infra/presubmit.py +++ b/infra/presubmit.py @@ -292,16 +292,11 @@ def is_nonfuzzer_python(path): return os.path.splitext(path)[1] == '.py' and '/projects/' not in path -def lint(paths): +def lint(): """Run python's linter on |paths| if it is a python file. Return False if it fails linting.""" - paths = [path for path in paths if is_nonfuzzer_python(path)] - if not paths: - return True - - command = ['python3', '-m', 'pylint', '-j', '0'] - command.extend(paths) + command = ['python3', '-m', 'pylint', '-j', '0', 'infra'] returncode = subprocess.run(command, check=False).returncode return returncode == 0 @@ -333,10 +328,10 @@ def get_changed_files(): ] -def run_tests(): +def run_tests(relevant_files): """Run all unit tests in directories that are different from HEAD.""" changed_dirs = set() - for file in get_changed_files(): + for file in relevant_files: changed_dirs.add(os.path.dirname(file)) # TODO(metzman): This approach for running tests is flawed since tests can @@ -384,7 +379,7 @@ def main(): return bool_to_returncode(success) if args.command == 'lint': - success = lint(relevant_files) + success = lint() return bool_to_returncode(success) if args.command == 'license': @@ -392,7 +387,7 @@ def main(): return bool_to_returncode(success) if args.command == 'infra-tests': - success = run_tests() + success = run_tests(relevant_files) return bool_to_returncode(success) # Do all the checks (but no tests). |