Skip to content

Commit 2c9d296

Browse files
authored
Move timeout subtraction outside Engine.fuzz. (#2108)
Currently it's not obvious at all to users of Engine.fuzz how long fuzzing will go for when a max_time is passed. Add Engine.fuzz_additional_processing_timeout so that the caller can explicitly subtract the necessary processing time themselves if needed. Remove engine_common.POSTPROCESSING_TIME. This was a holdout from the launcher days. This is moved to the AFL launcher and can be removed there as well once that's migrated to the Engine interface.
1 parent e6e7842 commit 2c9d296

File tree

9 files changed

+128
-70
lines changed

9 files changed

+128
-70
lines changed

src/python/bot/fuzzers/afl/launcher.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@
6464
# .options file option for the number of persistent executions.
6565
PERSISTENT_EXECUTIONS_OPTION = 'n'
6666

67+
# Grace period for the launcher to complete any processing before it's killed.
68+
# This will no longer be needed when we migrate to the engine interface.
69+
POSTPROCESSING_TIMEOUT = 30
70+
6771

6872
class AflOptionType(object):
6973
ARG = 0
@@ -1440,7 +1444,7 @@ def get_first_stacktrace(stderr_data):
14401444

14411445
def get_fuzz_timeout(is_mutations_run):
14421446
"""Get the maximum amount of time that should be spent fuzzing."""
1443-
hard_timeout = engine_common.get_hard_timeout()
1447+
hard_timeout = engine_common.get_hard_timeout() - POSTPROCESSING_TIMEOUT
14441448
merge_timeout = engine_common.get_merge_timeout(DEFAULT_MERGE_TIMEOUT)
14451449
fuzz_timeout = hard_timeout - merge_timeout
14461450
mutations_timeout = engine_common.get_new_testcase_mutations_timeout()

src/python/bot/fuzzers/engine_common.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,6 @@
5252
# file extension.
5353
SEED_CORPUS_ARCHIVE_SUFFIX = '_seed_corpus'
5454

55-
# Number of seconds to allow for extra processing.
56-
POSTPROCESSING_TIME = 30.0
57-
5855
# Maximum number of files in the corpus for which we will unpack the seed
5956
# corpus.
6057
MAX_FILES_FOR_UNPACK = 5
@@ -351,9 +348,7 @@ def get_hard_timeout(total_timeout=None):
351348
if total_timeout is None:
352349
total_timeout = environment.get_value('FUZZ_TEST_TIMEOUT')
353350

354-
# Give a small window of time to process (upload) the fuzzer output.
355-
hard_timeout = total_timeout - POSTPROCESSING_TIME
356-
return get_overridable_timeout(hard_timeout, 'HARD_TIMEOUT_OVERRIDE')
351+
return get_overridable_timeout(total_timeout, 'HARD_TIMEOUT_OVERRIDE')
357352

358353

359354
def get_merge_timeout(default_merge_timeout):

src/python/bot/fuzzers/libFuzzer/engine.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@ class LibFuzzerEngine(engine.Engine):
8686
def name(self):
8787
return 'libFuzzer'
8888

89+
def fuzz_additional_processing_timeout(self, options):
90+
"""Return the maximum additional timeout in seconds for additional
91+
operations in fuzz() (e.g. merging back new items).
92+
93+
Args:
94+
options: A FuzzOptions object.
95+
96+
Returns:
97+
An int representing the number of seconds required.
98+
"""
99+
fuzz_timeout = libfuzzer.get_fuzz_timeout(
100+
options.is_mutations_run, total_timeout=0)
101+
# get_fuzz_timeout returns a negative value.
102+
return -fuzz_timeout
103+
89104
def prepare(self, corpus_dir, target_path, build_dir):
90105
"""Prepare for a fuzzing session, by generating options. Returns a
91106
FuzzOptions object.
@@ -245,11 +260,9 @@ def fuzz(self, target_path, options, reproducers_dir, max_time):
245260
new_corpus_dir = self._create_temp_corpus_dir('new')
246261

247262
corpus_directories = [new_corpus_dir] + options.fuzz_corpus_dirs
248-
fuzz_timeout = libfuzzer.get_fuzz_timeout(
249-
options.is_mutations_run, total_timeout=max_time)
250263
fuzz_result = runner.fuzz(
251264
corpus_directories,
252-
fuzz_timeout=fuzz_timeout,
265+
fuzz_timeout=max_time,
253266
additional_args=options.arguments,
254267
artifact_prefix=reproducers_dir,
255268
extra_env=options.extra_env)
@@ -299,7 +312,7 @@ def fuzz(self, target_path, options, reproducers_dir, max_time):
299312
timeout_limit = fuzzer_utils.extract_argument(
300313
options.arguments, constants.TIMEOUT_FLAG, remove=False)
301314

302-
expected_duration = runner.get_max_total_time(fuzz_timeout)
315+
expected_duration = runner.get_max_total_time(max_time)
303316
actual_duration = int(fuzz_result.time_executed)
304317
fuzzing_time_percent = 100 * actual_duration / float(expected_duration)
305318
parsed_stats.update({

src/python/bot/tasks/fuzz_task.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,14 @@ def run_engine_fuzzer(engine_impl, target_name, sync_corpus_directory,
13011301
options = engine_impl.prepare(sync_corpus_directory, target_path, build_dir)
13021302

13031303
fuzz_test_timeout = environment.get_value('FUZZ_TEST_TIMEOUT')
1304+
additional_processing_time = engine_impl.fuzz_additional_processing_timeout(
1305+
options)
1306+
fuzz_test_timeout -= additional_processing_time
1307+
if fuzz_test_timeout <= 0:
1308+
raise FuzzTaskException(
1309+
f'Invalid engine timeout: '
1310+
f'{fuzz_test_timeout} - {additional_processing_time}')
1311+
13041312
result = engine_impl.fuzz(target_path, options, testcase_directory,
13051313
fuzz_test_timeout)
13061314

src/python/lib/clusterfuzz/fuzz/engine.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ def name(self):
7070
"""Get the name of the engine."""
7171
raise NotImplementedError
7272

73+
def fuzz_additional_processing_timeout(self, options):
74+
"""Return the maximum additional timeout in seconds for additional
75+
operations in fuzz() (e.g. merging back new items).
76+
77+
Args:
78+
options: A FuzzOptions object.
79+
80+
Returns:
81+
An int representing the number of seconds required.
82+
"""
83+
del options
84+
return 0
85+
7386
def prepare(self, corpus_dir, target_path, build_dir):
7487
"""Prepare for a fuzzing session, by generating options. Returns a
7588
FuzzOptions object.

src/python/tests/core/bot/fuzzers/afl/afl_launcher_test.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ class AflFuzzInputDirectoryTest(LauncherTestBase):
193193

194194
def setUp(self):
195195
self.temp_input_dir = os.path.join(self.TEMP_DIR, 'afl_input_dir')
196-
super(AflFuzzInputDirectoryTest, self).setUp()
196+
super().setUp()
197197
test_helpers.patch(self, ['bot.fuzzers.engine_common.is_lpm_fuzz_target'])
198198
self.mock.is_lpm_fuzz_target.return_value = True
199199
self.strategies = launcher.FuzzingStrategies(None)
@@ -315,7 +315,7 @@ class AflFuzzOutputDirectoryTest(LauncherTestBase):
315315
QUEUE_TESTCASE_INO = 2
316316

317317
def setUp(self):
318-
super(AflFuzzOutputDirectoryTest, self).setUp()
318+
super().setUp()
319319
# Creates self.OUTPUT_DIR.
320320
self.afl_output = launcher.AflFuzzOutputDirectory()
321321
os.mkdir(self.CRASHES_DIR)
@@ -462,7 +462,7 @@ def assert_sanitizer_opts_set(self, sanitizer_options_variable,
462462
self.assertIn(opt, sanitizer_options_value)
463463

464464
def setUp(self):
465-
super(SetSanitizerOptionsTest, self).setUp()
465+
super().setUp()
466466
test_helpers.patch_environ(self)
467467

468468
def test_left_unset(self):
@@ -515,7 +515,7 @@ class AflRunnerTest(LauncherTestBase):
515515
BAD_FILE_CONTENTS = 'bad'
516516

517517
def setUp(self):
518-
super(AflRunnerTest, self).setUp()
518+
super().setUp()
519519
test_helpers.patch_environ(self)
520520
test_helpers.patch(self, ['bot.fuzzers.engine_common.is_lpm_fuzz_target'])
521521
self.mock.is_lpm_fuzz_target.return_value = True
@@ -831,7 +831,7 @@ def test_validation(self):
831831
def test_correctness(self):
832832
"""Test that get_fuzz_timeout returns what we expect."""
833833
expected_fuzz_timeout = (
834-
self.valid_hard_timeout - engine_common.POSTPROCESSING_TIME -
834+
self.valid_hard_timeout - launcher.POSTPROCESSING_TIMEOUT -
835835
self.valid_merge_timeout)
836836

837837
self.call_helper(
@@ -845,7 +845,7 @@ class AflConfigTest(LauncherTestBase):
845845
"""Test AflConfig."""
846846

847847
def setUp(self):
848-
super(AflConfigTest, self).setUp()
848+
super().setUp()
849849
self.target_path = '/build_dir/target'
850850
self.options_path = self.target_path + '.options'
851851

@@ -927,7 +927,7 @@ class ListFullFilePathsTest(LauncherTestBase):
927927
DUMMY_3_FILENAME = 'dummyfile3'
928928

929929
def setUp(self):
930-
super(ListFullFilePathsTest, self).setUp()
930+
super().setUp()
931931
self.dummy_file_path = os.path.join(self.INPUT_DIR, fuzzer.AFL_DUMMY_INPUT)
932932
self.dummy_3_file_path, _ = self._create_file(self.DUMMY_3_FILENAME)
933933

src/python/tests/core/bot/fuzzers/engine_common_test.py

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ def function():
107107
def setUp(self):
108108
test_helpers.patch_environ(self)
109109
self.valid_hard_timeout = 500
110-
self.invalid_hard_timeout = engine_common.POSTPROCESSING_TIME - 1
111110
self.valid_merge_timeout = 15
112111

113112
def _set_environment_values(self, environment_variable_values):
@@ -151,28 +150,19 @@ def test_hard_timeout_override_validation(self):
151150
'HARD_TIMEOUT_OVERRIDE': -1
152151
})
153152

154-
def test_fuzz_test_timeout_validation(self):
155-
"""Test that get_hard_timeout rejects invalid values of
156-
FUZZ_TEST_TIMEOUT."""
157-
self.validation_helper({
158-
'FUZZ_TEST_TIMEOUT': engine_common.POSTPROCESSING_TIME - 1
159-
})
160-
161153
def test_hard_timeout_override_correctness(self):
162154
"""Test that get_hard_timeout returns what we expect when we set
163155
HARD_TIMEOUT_OVERRIDE."""
164-
self.call_helper(
165-
self.valid_hard_timeout, {
166-
'HARD_TIMEOUT_OVERRIDE': self.valid_hard_timeout,
167-
'FUZZ_TEST_TIMEOUT': self.invalid_hard_timeout
168-
})
156+
self.call_helper(self.valid_hard_timeout, {
157+
'HARD_TIMEOUT_OVERRIDE': self.valid_hard_timeout,
158+
'FUZZ_TEST_TIMEOUT': -1
159+
})
169160

170161
def test_fuzz_test_timeout_correctness(self):
171162
"""Test that get_hard_timeout returns what we expect when we set
172163
FUZZ_TEST_TIMEOUT."""
173-
self.call_helper(
174-
self.valid_hard_timeout - engine_common.POSTPROCESSING_TIME,
175-
{'FUZZ_TEST_TIMEOUT': self.valid_hard_timeout})
164+
self.call_helper(self.valid_hard_timeout,
165+
{'FUZZ_TEST_TIMEOUT': self.valid_hard_timeout})
176166

177167

178168
class GetMergeTimeoutTest(GetTimeoutTestBase):

0 commit comments

Comments
 (0)