aboutsummaryrefslogtreecommitdiff
path: root/infra/cifuzz
diff options
context:
space:
mode:
authorOliver Chang <oliverchang@users.noreply.github.com>2021-09-24 15:46:13 +1000
committerGitHub <noreply@github.com>2021-09-24 05:46:13 +0000
commit525e9eccd0d50a1c879c729dbdfda667f68038ad (patch)
tree0f87e0f0e9c1cc7721b82c0a52cbb77ce3d8d8ef /infra/cifuzz
parentaa8740a98acbb850e172dc7fcf32653ca0ce172a (diff)
downloadoss-fuzz-525e9eccd0d50a1c879c729dbdfda667f68038ad.tar.gz
Use libClusterFuzz for reproduction. (#6495)
Fixes #6326.
Diffstat (limited to 'infra/cifuzz')
-rw-r--r--infra/cifuzz/fuzz_target.py35
-rw-r--r--infra/cifuzz/fuzz_target_test.py54
2 files changed, 46 insertions, 43 deletions
diff --git a/infra/cifuzz/fuzz_target.py b/infra/cifuzz/fuzz_target.py
index 85486b0b9..2a6936b0e 100644
--- a/infra/cifuzz/fuzz_target.py
+++ b/infra/cifuzz/fuzz_target.py
@@ -17,13 +17,8 @@ import logging
import os
import shutil
import stat
-import sys
-import base_runner_utils
import config_utils
-# pylint: disable=wrong-import-position,import-error
-sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-import utils
import clusterfuzz.environment
import clusterfuzz.fuzz
@@ -39,6 +34,8 @@ LIBFUZZER_OPTIONS = ['-seed=1337', '-len_control=0']
# The number of reproduce attempts for a crash.
REPRODUCE_ATTEMPTS = 10
+REPRODUCE_TIME_SECONDS = 30
+
# Seconds on top of duration until a timeout error is raised.
BUFFER_TIME = 10
@@ -213,21 +210,25 @@ class FuzzTarget: # pylint: disable=too-many-instance-attributes
os.chmod(target_path, stat.S_IRWXO)
- env = base_runner_utils.get_env(self.config, self.workspace)
- env['TESTCASE'] = testcase
- command = ['reproduce', self.target_name, '-runs=100']
-
- logging.info('Running reproduce command: %s.', ' '.join(command))
- for _ in range(REPRODUCE_ATTEMPTS):
- _, _, returncode = utils.execute(command, env=env)
+ logging.info('Trying to reproduce crash using: %s.', testcase)
+ with clusterfuzz.environment.Environment(config_utils.DEFAULT_ENGINE,
+ self.config.sanitizer,
+ target_path,
+ interactive=True):
+ for _ in range(REPRODUCE_ATTEMPTS):
+ engine_impl = clusterfuzz.fuzz.get_engine(config_utils.DEFAULT_ENGINE)
+ result = engine_impl.reproduce(target_path,
+ testcase,
+ arguments=[],
+ max_time=REPRODUCE_TIME_SECONDS)
- if returncode != 0:
- logging.info('Reproduce command returned: %s. Reproducible on %s.',
- returncode, target_path)
+ if result.return_code != 0:
+ logging.info('Reproduce command returned: %s. Reproducible on %s.',
+ result.return_code, target_path)
- return True
+ return True
- logging.info('Reproduce command returned 0. Not reproducible on %s.',
+ logging.info('Reproduce command returned: 0. Not reproducible on %s.',
target_path)
return False
diff --git a/infra/cifuzz/fuzz_target_test.py b/infra/cifuzz/fuzz_target_test.py
index 3eafdc497..ecea6fbbf 100644
--- a/infra/cifuzz/fuzz_target_test.py
+++ b/infra/cifuzz/fuzz_target_test.py
@@ -19,8 +19,12 @@ import unittest
from unittest import mock
import certifi
+# Importing this later causes import failures with pytest for some reason.
+# TODO(ochang): Figure out why.
import parameterized
+import google.cloud.ndb # pylint: disable=unused-import
from pyfakefs import fake_filesystem_unittest
+from clusterfuzz.fuzz import engine
import clusterfuzz_deployment
import fuzz_target
@@ -34,11 +38,9 @@ EXAMPLE_PROJECT = 'example'
# An example fuzzer that triggers an error.
EXAMPLE_FUZZER = 'example_crash_fuzzer'
-# The return value of a successful call to utils.execute.
-EXECUTE_SUCCESS_RETVAL = ('', '', 0)
-
-# The return value of a failed call to utils.execute.
-EXECUTE_FAILURE_RETVAL = ('', '', 1)
+# Mock return values for engine_impl.reproduce.
+EXECUTE_SUCCESS_RESULT = engine.ReproduceResult([], 0, 0, '')
+EXECUTE_FAILURE_RESULT = engine.ReproduceResult([], 1, 0, '')
def _create_config(**kwargs):
@@ -85,40 +87,39 @@ class IsReproducibleTest(fake_filesystem_unittest.TestCase):
self.workspace, deployment,
deployment.config)
+ # ClusterFuzz requires ROOT_DIR.
+ root_dir = os.environ['ROOT_DIR']
test_helpers.patch_environ(self, empty=True)
+ os.environ['ROOT_DIR'] = root_dir
def test_reproducible(self, _):
"""Tests that is_reproducible returns True if crash is detected and that
is_reproducible uses the correct command to reproduce a crash."""
- all_repro = [EXECUTE_FAILURE_RETVAL] * fuzz_target.REPRODUCE_ATTEMPTS
- with mock.patch('utils.execute', side_effect=all_repro) as mock_execute:
+ all_repro = [EXECUTE_FAILURE_RESULT] * fuzz_target.REPRODUCE_ATTEMPTS
+ with mock.patch('clusterfuzz.fuzz.get_engine') as mock_get_engine:
+ mock_get_engine().reproduce.side_effect = all_repro
+
result = self.target.is_reproducible(self.testcase_path,
self.fuzz_target_path)
- expected_command = ['reproduce', 'fuzz-target', '-runs=100']
- expected_env = {
- 'SANITIZER': self.config.sanitizer,
- 'FUZZING_LANGUAGE': 'c++',
- 'OUT': self.workspace.out,
- 'CIFUZZ': 'True',
- 'FUZZING_ENGINE': 'libfuzzer',
- 'ARCHITECTURE': 'x86_64',
- 'TESTCASE': self.testcase_path,
- 'FUZZER_ARGS': '-rss_limit_mb=2560 -timeout=25'
- }
- mock_execute.assert_called_once_with(expected_command, env=expected_env)
+ mock_get_engine().reproduce.assert_called_once_with(
+ '/workspace/build-out/fuzz-target',
+ '/testcase',
+ arguments=[],
+ max_time=30)
self.assertTrue(result)
- self.assertEqual(1, mock_execute.call_count)
+ self.assertEqual(1, mock_get_engine().reproduce.call_count)
def test_flaky(self, _):
"""Tests that is_reproducible returns True if crash is detected on the last
attempt."""
- last_time_repro = [EXECUTE_SUCCESS_RETVAL] * 9 + [EXECUTE_FAILURE_RETVAL]
- with mock.patch('utils.execute',
- side_effect=last_time_repro) as mock_execute:
+ last_time_repro = [EXECUTE_SUCCESS_RESULT] * 9 + [EXECUTE_FAILURE_RESULT]
+ with mock.patch('clusterfuzz.fuzz.get_engine') as mock_get_engine:
+ mock_get_engine().reproduce.side_effect = last_time_repro
self.assertTrue(
self.target.is_reproducible(self.testcase_path,
self.fuzz_target_path))
- self.assertEqual(fuzz_target.REPRODUCE_ATTEMPTS, mock_execute.call_count)
+ self.assertEqual(fuzz_target.REPRODUCE_ATTEMPTS,
+ mock_get_engine().reproduce.call_count)
def test_nonexistent_fuzzer(self, _):
"""Tests that is_reproducible raises an error if it could not attempt
@@ -129,8 +130,9 @@ class IsReproducibleTest(fake_filesystem_unittest.TestCase):
def test_unreproducible(self, _):
"""Tests that is_reproducible returns False for a crash that did not
reproduce."""
- all_unrepro = [EXECUTE_SUCCESS_RETVAL] * fuzz_target.REPRODUCE_ATTEMPTS
- with mock.patch('utils.execute', side_effect=all_unrepro):
+ all_unrepro = [EXECUTE_SUCCESS_RESULT] * fuzz_target.REPRODUCE_ATTEMPTS
+ with mock.patch('clusterfuzz.fuzz.get_engine') as mock_get_engine:
+ mock_get_engine().reproduce.side_effect = all_unrepro
result = self.target.is_reproducible(self.testcase_path,
self.fuzz_target_path)
self.assertFalse(result)