Skip to content

Commit

Permalink
combine afl crash dirs into one, closes trailofbits#328
Browse files Browse the repository at this point in the history
  • Loading branch information
Travmatth committed Mar 19, 2020
1 parent a22ff7e commit 4ca88d6
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 2 deletions.
38 changes: 36 additions & 2 deletions bin/deepstate/executors/fuzz/afl.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
import os
import logging
import argparse
import re
import shutil
from collections import defaultdict


from typing import List, Dict, Optional

Expand Down Expand Up @@ -174,14 +178,44 @@ def _sync_seeds(self, src, dest, excludes=[]) -> None:
super()._sync_seeds(src, dest, excludes=excludes)


def consolidate_crash_dirs(self) -> None:
regex = re.compile(".*crashes.*")
crash_files = defaultdict(set)
crash_files_path = os.path.join(self.output_test_dir, "the_fuzzer")
for directory in os.listdir(crash_files_path):
crash_dir = os.path.join(crash_files_path, directory)
L.debug("Inspecting AFL crash dir: %s", crash_dir)
if not os.path.isdir(crash_dir) or not regex.match(crash_dir):
continue
for f in os.listdir(crash_dir):
crash_path = os.path.join(crash_dir, f)
if f == "README.txt" or not os.path.isfile(crash_path):
continue
crash_files[f].add(directory)
dirs_to_delete = set()
for name, paths in crash_files.items():
for p in paths:
if p == "crashes":
continue
dirs_to_delete.add(os.path.join(crash_files_path, p))
old = os.path.join(crash_files_path, p, name)
new = os.path.join(crash_files_path, "crashes", name)
L.debug("Moving crash report %s to %s", old, new)
os.rename(old, new)
for d in dirs_to_delete:
L.debug("Deleting crash directory %s", d)
shutil.rmtree(d)


def post_exec(self) -> None:
"""
AFL post_exec outputs last updated fuzzer stats,
and (TODO) performs crash triaging with seeds from
both sync_dir and local queue.
both sync_dir and local queue. Merges
output_test_dir/the_fuzzer/crashes* into one dir
"""
# TODO: merge output_test_dir/the_fuzzer/crashes* into one dir
super().post_exec()
self.consolidate_crash_dirs()


def main():
Expand Down
51 changes: 51 additions & 0 deletions tests/test_afl_combine_crashes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import os
import shutil
import logging
from unittest import TestCase
from deepstate.executors.fuzz.afl import AFL


from typing import Text


L = logging.getLogger(__name__)


expected_contents = set(["0", "1", "2", "3",
"4", "5", "6", "7",
"8", "README.txt"])


def create_crash_dirs(path: Text) -> None:
dir_i, crash_i = 0, 0
while dir_i < 3:
i, crash_dir = 0, os.path.join(path, "crashes")
if dir_i:
crash_dir += ".dir_" + str(dir_i)
os.mkdir(crash_dir, 0o777)
crash_file = os.path.join(crash_dir, "README.txt")
with open(crash_file, "w") as f:
f.write(crash_file)
while i < 3:
crash_file = os.path.join(crash_dir, str(crash_i))
with open(crash_file, "w") as f:
f.write(crash_file)
i, crash_i = i + 1, crash_i + 1
dir_i += 1


class AFLCombineCrashDirsTest(TestCase):
def test_combine_crash_directories(self):
afl = AFL("deepstate-afl")
afl.output_test_dir = os.path.join(os.getcwd(), "tests")
crash_out_dir = os.path.join(afl.output_test_dir, "the_fuzzer")
os.mkdir(crash_out_dir, 0o777)
create_crash_dirs(crash_out_dir)
afl.consolidate_crash_dirs()
contents = set()
for directory in os.listdir(crash_out_dir):
crash_dir = os.path.join(crash_out_dir, directory)
for f in os.listdir(crash_dir):
contents.add(f)
shutil.rmtree(crash_out_dir)
self.assertEqual(len(expected_contents - contents), 0)

0 comments on commit 4ca88d6

Please sign in to comment.