Skip to content

Commit

Permalink
Add dataflow tracing strategy and refactor strategy parsing code (#503)…
Browse files Browse the repository at this point in the history
…. (#583)

* Add dataflow tracing strategy to libFuzzer launcher (#503).

* Address review feedback, use new strategy code, refactor strategy parsing for stats
  • Loading branch information
Dor1s committed Jun 28, 2019
1 parent 470cf38 commit f24eb47
Show file tree
Hide file tree
Showing 7 changed files with 444 additions and 58 deletions.
2 changes: 2 additions & 0 deletions src/python/bot/fuzzers/libFuzzer/constants.py
Expand Up @@ -22,6 +22,8 @@
# libFuzzer flags.
ARTIFACT_PREFIX_FLAG = '-artifact_prefix='

COLLECT_DATA_FLOW_FLAG = '-collect_data_flow='

DICT_FLAG = '-dict='

FORK_FLAG = '-fork='
Expand Down
37 changes: 25 additions & 12 deletions src/python/bot/fuzzers/libFuzzer/launcher.py
Expand Up @@ -61,6 +61,9 @@
# Maximum length of a random chosen length for `-max_len`.
MAX_VALUE_FOR_MAX_LENGTH = 10000

# Probability of doing DFT-based fuzzing (depends on DFSan build presence).
DATAFLOW_TRACING_PROBABILITY = 0.25

# Number of radamsa mutations.
RADAMSA_MUTATIONS = 2000

Expand Down Expand Up @@ -429,17 +432,14 @@ def get_radamsa_path():

def remove_fuzzing_arguments(arguments):
"""Remove arguments used during fuzzing."""
# Remove un-needed dictionary argument.
fuzzer_utils.extract_argument(arguments, constants.DICT_FLAG)

# 'max_len' option may shrink the testcase if its size greater than 'max_len'.
fuzzer_utils.extract_argument(arguments, constants.MAX_LEN_FLAG)

# Remove custom '-runs' argument.
fuzzer_utils.extract_argument(arguments, constants.RUNS_FLAG)

# Remove `-fork' argument since it overrides '-merge' argument.
fuzzer_utils.extract_argument(arguments, constants.FORK_FLAG)
for argument in [
constants.DICT_FLAG, # User for fuzzing only.
constants.MAX_LEN_FLAG, # This may shrink the testcases.
constants.RUNS_FLAG, # Make sure we don't have any '-runs' argument.
constants.FORK_FLAG, # It overrides `-merge` argument.
constants.COLLECT_DATA_FLOW_FLAG, # Used for fuzzing only.
]:
fuzzer_utils.extract_argument(arguments, argument)


def load_testcase_if_exists(fuzzer_runner,
Expand Down Expand Up @@ -832,7 +832,20 @@ def main(argv):
arguments.append(constants.VALUE_PROFILE_ARGUMENT)
fuzzing_strategies.append(strategy.VALUE_PROFILE_STRATEGY.name)

if strategy_pool.do_strategy(strategy.FORK_STRATEGY):
# Depends on the presense of DFSan instrumented build.
dataflow_build_dir = environment.get_value('DATAFLOW_BUILD_DIR')
use_dataflow_tracing = (
dataflow_build_dir and
strategy_pool.do_strategy(strategy.DATAFLOW_TRACING_STRATEGY))
if use_dataflow_tracing:
dataflow_binary_path = os.path.join(dataflow_build_dir,
os.path.basename(fuzzer_path))
arguments.append(
'%s%s' % (constants.COLLECT_DATA_FLOW_FLAG, dataflow_binary_path))
fuzzing_strategies.append(strategy.DATAFLOW_TRACING_STRATEGY.name)

# DataFlow Tracing requires fork mode, always use it with DFT strategy.
if use_dataflow_tracing or strategy_pool.do_strategy(strategy.FORK_STRATEGY):
max_fuzz_threads = environment.get_value('MAX_FUZZ_THREADS', 1)
num_fuzz_processes = max(1, multiprocessing.cpu_count() // max_fuzz_threads)
arguments.append('%s%d' % (constants.FORK_FLAG, num_fuzz_processes))
Expand Down
41 changes: 17 additions & 24 deletions src/python/bot/fuzzers/libFuzzer/stats.py
Expand Up @@ -110,6 +110,11 @@ def calculate_log_lines(log_lines):
return other_lines_count, libfuzzer_lines_count, ignored_lines_count


def strategy_column_name(strategy_name):
"""Convert the strategy name into stats column name."""
return 'strategy_%s' % strategy_name


def parse_fuzzing_strategies(log_lines, strategies):
"""Extract stats for fuzzing strategies used."""
if not strategies:
Expand All @@ -130,27 +135,19 @@ def parse_line_for_strategy_prefix(line, strategy_name):

try:
strategy_value = int(line[len(strategy_prefix):])
stats['strategy_' + strategy_name] = strategy_value
stats[strategy_column_name(strategy_name)] = strategy_value
except (IndexError, ValueError) as e:
logs.log_error('Failed to parse strategy "%s":\n%s\n' % (line, str(e)))

for line in strategies:
parse_line_for_strategy_prefix(line, strategy.CORPUS_SUBSET_STRATEGY.name)
parse_line_for_strategy_prefix(line, strategy.FORK_STRATEGY.name)
# These strategies are used with different values specified in the prefix.
for strategy_type in strategy.strategies_with_prefix_value:
for line in strategies:
parse_line_for_strategy_prefix(line, strategy_type.name)

# Other strategies are either ON or OFF, without arbitrary values.
if strategy.CORPUS_MUTATION_RADAMSA_STRATEGY.name in strategies:
stats['strategy_corpus_mutations_radamsa'] = 1
if strategy.CORPUS_MUTATION_ML_RNN_STRATEGY.name in strategies:
stats['strategy_corpus_mutations_ml_rnn'] = 1
if strategy.MUTATOR_PLUGIN_STRATEGY.name in strategies:
stats['strategy_mutator_plugin'] = 1
if strategy.RANDOM_MAX_LENGTH_STRATEGY.name in strategies:
stats['strategy_random_max_len'] = 1
if strategy.RECOMMENDED_DICTIONARY_STRATEGY.name in strategies:
stats['strategy_recommended_dict'] = 1
if strategy.VALUE_PROFILE_STRATEGY.name in strategies:
stats['strategy_value_profile'] = 1
for strategy_type in strategy.strategies_with_boolean_value:
if strategy_type.name in strategies:
stats[strategy_column_name(strategy_type.name)] = 1

return stats

Expand Down Expand Up @@ -183,17 +180,13 @@ def parse_performance_features(log_lines, strategies, arguments):
'slow_unit_count': 0,
'slow_units_count': 0,
'startup_crash_count': 1,
'strategy_corpus_mutations_radamsa': 0,
'strategy_corpus_mutations_ml_rnn': 0,
'strategy_corpus_subset': 0,
'strategy_fork': 0,
'strategy_mutator_plugin': 0,
'strategy_random_max_len': 0,
'strategy_recommended_dict': 0,
'strategy_value_profile': 0,
'timeout_count': 0,
}

# Initialize all strategy stats as disabled by default.
for strategy_type in strategy.strategy_list:
stats[strategy_column_name(strategy_type.name)] = 0

# Process fuzzing strategies used.
stats.update(parse_fuzzing_strategies(log_lines, strategies))

Expand Down
30 changes: 16 additions & 14 deletions src/python/bot/fuzzers/libFuzzer/strategy_selection.py
Expand Up @@ -82,16 +82,15 @@ def generate_default_strategy_pool():
# Decide whether or not to include remaining strategies.
if engine_common.do_corpus_subset():
pool.add_strategy(strategy.CORPUS_SUBSET_STRATEGY)
if do_strategy(strategy.RANDOM_MAX_LENGTH_STRATEGY):
pool.add_strategy(strategy.RANDOM_MAX_LENGTH_STRATEGY)
if do_strategy(strategy.RECOMMENDED_DICTIONARY_STRATEGY):
pool.add_strategy(strategy.RECOMMENDED_DICTIONARY_STRATEGY)
if do_strategy(strategy.VALUE_PROFILE_STRATEGY):
pool.add_strategy(strategy.VALUE_PROFILE_STRATEGY)
if do_strategy(strategy.FORK_STRATEGY):
pool.add_strategy(strategy.FORK_STRATEGY)
if do_strategy(strategy.MUTATOR_PLUGIN_STRATEGY):
pool.add_strategy(strategy.MUTATOR_PLUGIN_STRATEGY)

for value in [
strategy.RANDOM_MAX_LENGTH_STRATEGY,
strategy.RECOMMENDED_DICTIONARY_STRATEGY, strategy.VALUE_PROFILE_STRATEGY,
strategy.FORK_STRATEGY, strategy.MUTATOR_PLUGIN_STRATEGY
]:
if do_strategy(value):
pool.add_strategy(value)

return pool


Expand All @@ -117,9 +116,12 @@ def generate_weighted_strategy_pool():
if strategy_tuple.name in chosen_strategies:
pool.add_strategy(strategy_tuple)

# We consider mutator plugin separately as it is only supported by a small
# number of fuzz targets and should be used heavily when available.
if do_strategy(strategy.MUTATOR_PLUGIN_STRATEGY):
pool.add_strategy(strategy.MUTATOR_PLUGIN_STRATEGY)
# We consider certain strategies separately as those are only supported by a
# small number of fuzz targets and should be used heavily when available.
for value in [
strategy.DATAFLOW_TRACING_STRATEGY, strategy.MUTATOR_PLUGIN_STRATEGY
]:
if do_strategy(value):
pool.add_strategy(value)

return pool
5 changes: 5 additions & 0 deletions src/python/bot/fuzzers/libfuzzer.py
Expand Up @@ -550,6 +550,7 @@ def get_runner(fuzzer_path, temp_dir=None):
"""Get a libfuzzer runner."""
use_minijail = environment.get_value('USE_MINIJAIL')
build_dir = environment.get_value('BUILD_DIR')
dataflow_build_dir = environment.get_value('DATAFLOW_BUILD_DIR')
if use_minijail:
# Set up chroot and runner.
if environment.is_chromeos_system_job():
Expand All @@ -564,6 +565,10 @@ def get_runner(fuzzer_path, temp_dir=None):
minijail_chroot.add_binding(
minijail.ChrootBinding(build_dir, build_dir, False))

if dataflow_build_dir:
minijail_chroot.add_binding(
minijail.ChrootBinding(dataflow_build_dir, dataflow_build_dir, False))

# Also bind the build dir to /out to make it easier to hardcode references
# to data files.
minijail_chroot.add_binding(
Expand Down
26 changes: 23 additions & 3 deletions src/python/bot/fuzzers/strategy.py
Expand Up @@ -26,6 +26,7 @@
name='corpus_mutations_radamsa', probability=0.15)
CORPUS_MUTATION_ML_RNN_STRATEGY = Strategy(
name='corpus_mutations_ml_rnn', probability=0.50)
DATAFLOW_TRACING_STRATEGY = Strategy(name='dataflow_tracing', probability=0.25)
CORPUS_SUBSET_STRATEGY = Strategy(name='corpus_subset', probability=0.50)
FORK_STRATEGY = Strategy(name='fork', probability=0.50)
MUTATOR_PLUGIN_STRATEGY = Strategy(name='mutator_plugin', probability=0.50)
Expand All @@ -36,7 +37,26 @@

strategy_list = [
CORPUS_MUTATION_RADAMSA_STRATEGY, CORPUS_MUTATION_ML_RNN_STRATEGY,
CORPUS_SUBSET_STRATEGY, FORK_STRATEGY, MUTATOR_PLUGIN_STRATEGY,
RANDOM_MAX_LENGTH_STRATEGY, RECOMMENDED_DICTIONARY_STRATEGY,
VALUE_PROFILE_STRATEGY
DATAFLOW_TRACING_STRATEGY, CORPUS_SUBSET_STRATEGY, FORK_STRATEGY,
MUTATOR_PLUGIN_STRATEGY, RANDOM_MAX_LENGTH_STRATEGY,
RECOMMENDED_DICTIONARY_STRATEGY, VALUE_PROFILE_STRATEGY
]

strategies_with_prefix_value = [
CORPUS_SUBSET_STRATEGY,
FORK_STRATEGY,
]

strategies_with_boolean_value = [
CORPUS_MUTATION_RADAMSA_STRATEGY,
CORPUS_MUTATION_ML_RNN_STRATEGY,
DATAFLOW_TRACING_STRATEGY,
MUTATOR_PLUGIN_STRATEGY,
RANDOM_MAX_LENGTH_STRATEGY,
RECOMMENDED_DICTIONARY_STRATEGY,
VALUE_PROFILE_STRATEGY,
]

# To ensure that all strategies present in |strategy_list| are parsed for stats.
assert (set(strategy_list) == set(strategies_with_prefix_value +
strategies_with_boolean_value))

0 comments on commit f24eb47

Please sign in to comment.