diff --git a/.gitignore b/.gitignore index e752b94a2..fcae925db 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ docker/generated.mk # Vim backup files. .*.swp + +# Diff files from matplotlib +*-failed-diff.png diff --git a/analysis/test_data/pairwise_unique_coverage_heatmap-failed-diff.png b/analysis/test_data/pairwise_unique_coverage_heatmap-failed-diff.png deleted file mode 100644 index 5dc618e44..000000000 Binary files a/analysis/test_data/pairwise_unique_coverage_heatmap-failed-diff.png and /dev/null differ diff --git a/common/experiment_utils.py b/common/experiment_utils.py index 3911751ac..30668ab12 100644 --- a/common/experiment_utils.py +++ b/common/experiment_utils.py @@ -129,6 +129,11 @@ def get_corpus_archive_name(cycle: int) -> str: return get_cycle_filename('corpus-archive', cycle) + '.tar.gz' +def get_coverage_archive_name(cycle: int) -> str: + """Returns a corpus archive name given a cycle.""" + return get_cycle_filename('coverage-archive', cycle) + '.json' + + def get_stats_filename(cycle: int) -> str: """Returns a corpus archive name given a cycle.""" return get_cycle_filename('stats', cycle) + '.json' diff --git a/common/fuzzer_utils.py b/common/fuzzer_utils.py index 049ba8ce3..b47e2860f 100644 --- a/common/fuzzer_utils.py +++ b/common/fuzzer_utils.py @@ -72,6 +72,12 @@ def dockerfiles(self): def get_fuzz_target_binary(search_directory: str, fuzz_target_name: str) -> Optional[str]: """Return target binary path.""" + logs.info(f'Searching for fuzz target binary named {fuzz_target_name} under' + f' directory {search_directory}') + logs.info(f'Search diretory {os.path.abspath(search_directory)} exists: ' + f'{os.path.exists(os.path.abspath(search_directory))}') + logs.info(f'list Search diretory {search_directory}: ' + f'{os.listdir(search_directory)}') if fuzz_target_name: fuzz_target_binary = os.path.join(search_directory, fuzz_target_name) if os.path.exists(fuzz_target_binary): @@ -83,7 +89,11 @@ def get_fuzz_target_binary(search_directory: str, if os.path.exists(default_fuzz_target_binary): return default_fuzz_target_binary + logs.info('Searching for possible fuzz target in search directory: ' + f'{search_directory}') for root, _, files in os.walk(search_directory): + logs.info(f'Searching for possible fuzz target under subdir {root}: ' + f'{files}') if root == 'uninstrumented': continue for filename in files: diff --git a/experiment/build/gcb_build.py b/experiment/build/gcb_build.py index f553848c7..8030f23dd 100644 --- a/experiment/build/gcb_build.py +++ b/experiment/build/gcb_build.py @@ -105,7 +105,8 @@ def _build( # TODO(metzman): Refactor code so that local_build stores logs as well. build_utils.store_build_logs(config_name, result) if result.retcode != 0: - logs.error('%s failed.', command) + logs.error('%s failed. Return code: %d. Output: %s. Timedout: %s', + command, result.retcode, result.output, result.timed_out) raise subprocess.CalledProcessError(result.retcode, command) return result diff --git a/experiment/measurer/coverage_utils.py b/experiment/measurer/coverage_utils.py index 27ab69965..9f8e295c9 100644 --- a/experiment/measurer/coverage_utils.py +++ b/experiment/measurer/coverage_utils.py @@ -133,7 +133,9 @@ def merge_profdata_files(self): result = merge_profdata_files(files_to_merge, self.merged_profdata_file) if result.retcode != 0: - logger.error('Profdata files merging failed.') + logger.error( + f'Profdata files merging failed for (fuzzer, benchmark): ' + f'({self.fuzzer}, {self.benchmark}).') def generate_coverage_summary_json(self): """Generates the coverage summary json from merged profdata file.""" diff --git a/experiment/measurer/measure_manager.py b/experiment/measurer/measure_manager.py index 288148401..b3813653d 100644 --- a/experiment/measurer/measure_manager.py +++ b/experiment/measurer/measure_manager.py @@ -16,6 +16,7 @@ import collections import gc import glob +import gzip import multiprocessing import json import os @@ -614,10 +615,33 @@ def measure_snapshot_coverage( # pylint: disable=too-many-locals # Generate profdata and transform it into json form. snapshot_measurer.generate_coverage_information(cycle) + # Compress and save the exported profdata snapshot. + coverage_archive_zipped = os.path.join( + snapshot_measurer.trial_dir, 'coverage', + experiment_utils.get_coverage_archive_name(cycle) + '.gz') + + coverage_archive_dir = os.path.dirname(coverage_archive_zipped) + if not os.path.exists(coverage_archive_dir): + os.makedirs(coverage_archive_dir) + + with gzip.open(str(coverage_archive_zipped), 'wb') as compressed: + with open(snapshot_measurer.cov_summary_file, 'rb') as uncompressed: + # avoid saving warnings so we can direct import with pandas + compressed.write(uncompressed.readlines()[-1]) + + coverage_archive_dst = exp_path.filestore(coverage_archive_zipped) + if filestore_utils.cp(coverage_archive_zipped, + coverage_archive_dst, + expect_zero=False).retcode: + snapshot_logger.warning('Coverage not found for cycle: %d.', cycle) + return None + + os.remove(coverage_archive_zipped) # no reason to keep this around + # Run crashes again, parse stacktraces and generate crash signatures. crashes = snapshot_measurer.process_crashes(cycle) - # Get the coverage of the new corpus units. + # Get the coverage summary of the new corpus units. branches_covered = snapshot_measurer.get_current_coverage() fuzzer_stats_data = snapshot_measurer.get_fuzzer_stats(cycle) snapshot = models.Snapshot(time=this_time, diff --git a/experiment/runner.py b/experiment/runner.py index b8c95fcca..776a4656e 100644 --- a/experiment/runner.py +++ b/experiment/runner.py @@ -177,10 +177,13 @@ def run_fuzzer(max_total_time, log_filename): input_corpus = environment.get('SEED_CORPUS_DIR') output_corpus = os.environ['OUTPUT_CORPUS_DIR'] fuzz_target_name = environment.get('FUZZ_TARGET') + logs.info('all ENV VAR ' + f'{[f"{key}: {value}" for key, value in os.environ.items()]}') target_binary = fuzzer_utils.get_fuzz_target_binary(FUZZ_TARGET_DIR, fuzz_target_name) if not target_binary: - logs.error('Fuzz target binary not found.') + logs.error(f'Fuzz target binary {fuzz_target_name} not found under ' + f'{FUZZ_TARGET_DIR}') return if max_total_time is None: diff --git a/fuzzers/libafl/builder.Dockerfile b/fuzzers/libafl/builder.Dockerfile index 7fd7462a7..f0136ff46 100644 --- a/fuzzers/libafl/builder.Dockerfile +++ b/fuzzers/libafl/builder.Dockerfile @@ -38,7 +38,7 @@ RUN wget https://gist.githubusercontent.com/tokatoka/26f4ba95991c6e3313999997633 RUN git clone https://github.com/AFLplusplus/LibAFL /libafl # Checkout a current commit -RUN cd /libafl && git pull && git checkout f3433767bea0cc3d7ee3b4c08be138e61d20c468 || true +RUN cd /libafl && git pull && git checkout f856092f3d393056b010fcae3b086769377cba18 || true # Note that due a nightly bug it is currently fixed to a known version on top! # Compile libafl. diff --git a/requirements.txt b/requirements.txt index 56b835357..ca7b933cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ redis==4.3.4 rq==1.11.1 scikit-posthocs==0.7.0 scipy==1.9.2 -seaborn==0.12.0 +seaborn==0.13.2 sqlalchemy==1.4.41 protobuf==3.20.3 diff --git a/service/gcbrun_experiment.py b/service/gcbrun_experiment.py index f19ab493d..bbebcf1b9 100644 --- a/service/gcbrun_experiment.py +++ b/service/gcbrun_experiment.py @@ -28,7 +28,6 @@ TRIGGER_COMMAND = '/gcbrun' RUN_EXPERIMENT_COMMAND_STR = f'{TRIGGER_COMMAND} run_experiment.py ' SKIP_COMMAND_STR = f'{TRIGGER_COMMAND} skip' -# A DUMMY COMMENT def get_comments(pull_request_number):