Skip to content

Commit e58bcfc

Browse files
authored
Set a proper timeout for corpus minimization after a fuzzing session (google#4719)
This change also adds the collected new_units_added stat to the centipede schema.
1 parent e44a0a4 commit e58bcfc

File tree

5 files changed

+48
-32
lines changed

5 files changed

+48
-32
lines changed

src/clusterfuzz/_internal/bot/fuzzers/centipede/engine.py

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,19 @@ def _get_arguments(self, fuzzer_path):
188188

189189
return arguments
190190

191+
def fuzz_additional_processing_timeout(self, options):
192+
"""Return the maximum additional timeout in seconds for additional
193+
operations in fuzz() (e.g. merging back new items).
194+
195+
Args:
196+
options: A FuzzOptions object.
197+
198+
Returns:
199+
An int representing the number of seconds required.
200+
"""
201+
del options
202+
return engine_common.get_merge_timeout(engine_common.DEFAULT_MERGE_TIMEOUT)
203+
191204
# pylint: disable=unused-argument
192205
def prepare(self, corpus_dir, target_path, build_dir):
193206
"""Prepares for a fuzzing session, by generating options.
@@ -302,39 +315,14 @@ def fuzz(self, target_path, options, reproducers_dir, max_time): # pylint: disa
302315

303316
old_corpus_len = shell.get_directory_file_count(options.corpus_dir)
304317
logs.info(f'Corpus length before fuzzing: {old_corpus_len}')
305-
306318
fuzz_result = runner.run_and_wait(
307319
additional_args=options.arguments, timeout=timeout)
308320
log_lines = fuzz_result.output.splitlines()
309321
fuzz_result.output = Engine.trim_logs(fuzz_result.output)
310322

311-
workdir = options.workdir
312-
313-
try:
314-
time_for_minimize = timeout - fuzz_result.time_executed
315-
316-
self.minimize_corpus(
317-
target_path=target_path,
318-
arguments=[],
319-
# New units, in addition to the main corpus units,
320-
# are placed in new_corpus_dir. Minimize and merge back
321-
# to the main corpus_dir.
322-
input_dirs=[options.new_corpus_dir],
323-
output_dir=options.corpus_dir,
324-
reproducers_dir=reproducers_dir,
325-
max_time=time_for_minimize,
326-
# Use the same workdir that was used for fuzzing.
327-
# This allows us to skip rerunning the fuzzing inputs.
328-
workdir=workdir)
329-
except:
330-
# TODO(alhijazi): Convert to a warning if this becomes a problem
331-
# caused by user code rather than by ClusterFuzz or Centipede.
332-
logs.error('Corpus minimization failed.')
333-
# If we fail to minimize, fall back to moving the new units
334-
# from the new corpus_dir to the main corpus_dir.
335-
engine_common.move_mergeable_units(options.new_corpus_dir,
336-
options.corpus_dir)
323+
logs.info('Fuzzing run completed.', fuzzing_logs=log_lines)
337324

325+
workdir = options.workdir
338326
reproducer_path = _get_reproducer_path(fuzz_result.output, reproducers_dir)
339327
crashes = []
340328
if reproducer_path:
@@ -364,6 +352,30 @@ def fuzz(self, target_path, options, reproducers_dir, max_time): # pylint: disa
364352
stats['average_exec_per_sec'] = num_execs_avg / fuzz_time_secs_avg
365353
stats.update(_parse_centipede_logs(log_lines))
366354

355+
try:
356+
self.minimize_corpus(
357+
target_path=target_path,
358+
arguments=[],
359+
# New units, in addition to the main corpus units,
360+
# are placed in new_corpus_dir. Minimize and merge back
361+
# to the main corpus_dir.
362+
input_dirs=[options.new_corpus_dir],
363+
output_dir=options.corpus_dir,
364+
reproducers_dir=reproducers_dir,
365+
max_time=engine_common.get_merge_timeout(
366+
engine_common.DEFAULT_MERGE_TIMEOUT),
367+
# Use the same workdir that was used for fuzzing.
368+
# This allows us to skip rerunning the fuzzing inputs.
369+
workdir=workdir)
370+
except:
371+
# TODO(alhijazi): Convert to a warning if this becomes a problem
372+
# caused by user code rather than by ClusterFuzz or Centipede.
373+
logs.error('Corpus minimization failed.')
374+
# If we fail to minimize, fall back to moving the new units
375+
# from the new corpus_dir to the main corpus_dir.
376+
engine_common.move_mergeable_units(options.new_corpus_dir,
377+
options.corpus_dir)
378+
367379
new_corpus_len = shell.get_directory_file_count(options.corpus_dir)
368380
logs.info(f'Corpus length after fuzzing: {new_corpus_len}')
369381
new_units_added = new_corpus_len - old_corpus_len

src/clusterfuzz/_internal/bot/fuzzers/engine_common.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@
7474

7575
HEXDIGITS_SET = set(string.hexdigits)
7676

77+
# Allow 30 minutes to merge the testcases back into the corpus.
78+
DEFAULT_MERGE_TIMEOUT = 30 * 60
79+
7780

7881
class Generator:
7982
"""Generators we can use."""

src/clusterfuzz/_internal/bot/fuzzers/libFuzzer/engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def _merge_new_units(self, target_path, corpus_dir, new_corpus_dir,
235235
output_corpus_dir=merge_corpus,
236236
reproducers_dir=None,
237237
max_time=engine_common.get_merge_timeout(
238-
libfuzzer.DEFAULT_MERGE_TIMEOUT))
238+
engine_common.DEFAULT_MERGE_TIMEOUT))
239239

240240
engine_common.move_mergeable_units(merge_corpus, corpus_dir)
241241
new_corpus_len = shell.get_directory_file_count(corpus_dir)

src/clusterfuzz/_internal/bot/fuzzers/libfuzzer.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@
3939
# Maximum length of a random chosen length for `-max_len`.
4040
MAX_VALUE_FOR_MAX_LENGTH = 10000
4141

42-
# Allow 30 minutes to merge the testcases back into the corpus.
43-
DEFAULT_MERGE_TIMEOUT = 30 * 60
44-
4542
StrategyInfo = collections.namedtuple('StrategiesInfo', [
4643
'fuzzing_strategies',
4744
'arguments',
@@ -1296,7 +1293,7 @@ def get_fuzz_timeout(is_mutations_run, total_timeout=None):
12961293
"""Get the fuzz timeout."""
12971294
fuzz_timeout = (
12981295
engine_common.get_hard_timeout(total_timeout=total_timeout) -
1299-
engine_common.get_merge_timeout(DEFAULT_MERGE_TIMEOUT))
1296+
engine_common.get_merge_timeout(engine_common.DEFAULT_MERGE_TIMEOUT))
13001297

13011298
if is_mutations_run:
13021299
fuzz_timeout -= engine_common.get_new_testcase_mutations_timeout()

src/clusterfuzz/_internal/metrics/fuzzer_stats_schema.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,10 @@
855855
'mode': 'NULLABLE',
856856
'name': 'average_exec_per_sec',
857857
'type': 'FLOAT'
858+
}, {
859+
'mode': 'NULLABLE',
860+
'name': 'new_units_added',
861+
'type': 'INTEGER'
858862
}] + _COMMON_COLUMNS
859863

860864
_SCHEMA = {

0 commit comments

Comments
 (0)