[CIFuzz] Add parse fuzzer output functionality (#3342)

This commit is contained in:
Leo Neat 2020-02-10 10:00:54 -08:00 committed by GitHub
parent 891450ddec
commit ffc6af6d1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 260 additions and 6 deletions

View File

@ -63,11 +63,8 @@ def main():
# If the file does not exist, the action will crash attempting to upload it.
# The dry run needs this file because it is set to upload a test case both
# on successful runs and on failures.
out_dir = os.path.join(workspace, 'out')
out_dir = os.path.join(workspace, 'out', 'bug_report')
os.makedirs(out_dir, exist_ok=True)
file_handle = open(os.path.join(out_dir, 'testcase'), 'w')
file_handle.write('No bugs detected.')
file_handle.close()
# Sets the default return code on error to success.
error_code = 0

View File

@ -33,6 +33,35 @@ import helper
import repo_manager
import utils
# From clusterfuzz: src/python/crash_analysis/crash_analyzer.py
# Used to get the beginning of the stack trace.
STACKTRACE_TOOL_MARKERS = [
'AddressSanitizer',
'ASAN:',
'CFI: Most likely a control flow integrity violation;',
'ERROR: libFuzzer',
'KASAN:',
'LeakSanitizer',
'MemorySanitizer',
'ThreadSanitizer',
'UndefinedBehaviorSanitizer',
'UndefinedSanitizer',
]
# From clusterfuzz: src/python/crash_analysis/crash_analyzer.py
# Used to get the end of the stack trace.
STACKTRACE_END_MARKERS = [
'ABORTING',
'END MEMORY TOOL REPORT',
'End of process memory map.',
'END_KASAN_OUTPUT',
'SUMMARY:',
'Shadow byte and word',
'[end of stack trace]',
'\nExiting',
'minidump has been written',
]
# TODO: Turn default logging to WARNING when CIFuzz is stable
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
@ -141,6 +170,8 @@ def run_fuzzers(fuzz_seconds, workspace):
logging.error('Invalid workspace: %s.', workspace)
return False, False
out_dir = os.path.join(workspace, 'out')
bug_report_dir = os.path.join(out_dir, 'bug_report')
os.makedirs(bug_report_dir, exist_ok=True)
if not fuzz_seconds or fuzz_seconds < 1:
logging.error('Fuzz_seconds argument must be greater than 1, but was: %s.',
format(fuzz_seconds))
@ -164,6 +195,41 @@ def run_fuzzers(fuzz_seconds, workspace):
else:
logging.info('Fuzzer %s, detected error: %s.', target.target_name,
stack_trace)
shutil.move(test_case, os.path.join(out_dir, 'testcase'))
shutil.move(test_case, os.path.join(bug_report_dir, 'test_case'))
parse_fuzzer_output(stack_trace, bug_report_dir)
return True, True
return True, False
def parse_fuzzer_output(fuzzer_output, out_dir):
"""Parses the fuzzer output from a fuzz target binary.
Args:
fuzzer_output: A fuzz target binary output string to be parsed.
out_dir: The location to store the parsed output files.
"""
# Get index of key file points.
for marker in STACKTRACE_TOOL_MARKERS:
marker_index = fuzzer_output.find(marker)
if marker_index:
begin_summary = marker_index
break
end_summary = -1
for marker in STACKTRACE_END_MARKERS:
marker_index = fuzzer_output.find(marker)
if marker_index:
end_summary = marker_index + len(marker)
break
if begin_summary is None or end_summary is None:
return
summary_str = fuzzer_output[begin_summary:end_summary]
if not summary_str:
return
# Write sections of fuzzer output to specific files.
summary_file_path = os.path.join(out_dir, 'bug_summary.txt')
with open(summary_file_path, 'a') as summary_handle:
summary_handle.write(summary_str)

View File

@ -110,7 +110,7 @@ class BuildFuzzersIntegrationTest(unittest.TestCase):
class RunFuzzersIntegrationTest(unittest.TestCase):
"""Test build_fuzzers function in the utils module."""
"""Test build_fuzzers function in the cifuzz module."""
def test_valid(self):
"""Test run_fuzzers with a valid build."""
@ -153,5 +153,34 @@ class RunFuzzersIntegrationTest(unittest.TestCase):
self.assertFalse(bug_found)
class ParseOutputUnitTest(unittest.TestCase):
"""Test parse_fuzzer_output function in the cifuzz module."""
def parse_valid_output(self):
"""Checks that the parse fuzzer output can correctly parse output."""
test_case_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'test_files')
test_output_path = os.path.join(test_case_path, 'example_fuzzer_output.txt')
test_summary_path = os.path.join(test_case_path, 'bug_summary_example.txt')
with tempfile.TemporaryDirectory() as tmp_dir:
with open(test_output_path, 'r') as test_fuzz_output:
cifuzz.parse_fuzzer_output(test_fuzz_output.read(), tmp_dir)
result_files = ['bug_summary.txt']
self.assertCountEqual(os.listdir(tmp_dir), result_files)
# Compare the bug summaries.
with open(os.path.join(tmp_dir, 'bug_summary.txt'), 'r') as bug_summary:
detected_summary = bug_summary.read()
with open(os.path.join(test_summary_path), 'r') as bug_summary:
real_summary = bug_summary.read()
self.assertEqual(detected_summary, real_summary)
def parse_invalid_output(self):
"""Checks that no files are created when an invalid input was given."""
with tempfile.TemporaryDirectory() as tmp_dir:
cifuzz.parse_fuzzer_output('not a valid output_string', tmp_dir)
self.assertEqual(len(os.listdir(tmp_dir)), 0)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,68 @@
AddressSanitizer: heap-buffer-overflow on address 0x62500001b530 at pc 0x00000052138a bp 0x7ffe62db2c10 sp 0x7ffe62db23d8
READ of size 52 at 0x62500001b530 thread T0
SCARINESS: 26 (multi-byte-read-heap-buffer-overflow)
#0 0x521389 in __asan_memcpy /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
#1 0x567590 in yr_object_set_string /src/yara/libyara/object.c:1122:5
#2 0x5afced in dex_parse /src/yara/libyara/modules/dex/dex.c:781:5
#3 0x5b4a8b in dex__load /src/yara/libyara/modules/dex/dex.c:1218:7
#4 0x56537c in yr_modules_load /src/yara/libyara/modules.c:179:16
#5 0x5d6583 in yr_execute_code /src/yara/libyara/exec.c:1276:18
#6 0x56f5c0 in yr_scanner_scan_mem_blocks /src/yara/libyara/scanner.c:444:3
#7 0x56bf23 in yr_rules_scan_mem_blocks /src/yara/libyara/rules.c:235:12
#8 0x56c182 in yr_rules_scan_mem /src/yara/libyara/rules.c:285:10
#9 0x5548d2 in LLVMFuzzerTestOneInput /src/yara/tests/oss-fuzz/dex_fuzzer.cc:40:3
#10 0x45a3b1 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:556:15
#11 0x459ad5 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:470:3
#12 0x45be77 in fuzzer::Fuzzer::MutateAndTestOne() /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:698:19
#13 0x45cc05 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:830:5
#14 0x44ac88 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:824:6
#15 0x474ab2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19:10
#16 0x7f4409b7a82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#17 0x41e348 in _start (out/dex_fuzzer+0x41e348)
0x62500001b530 is located 0 bytes to the right of 9264-byte region [0x625000019100,0x62500001b530)
allocated by thread T0 here:
#0 0x521f4d in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:145:3
#1 0x4331b7 in operator new(unsigned long) (out/dex_fuzzer+0x4331b7)
#2 0x459ad5 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:470:3
#3 0x45be77 in fuzzer::Fuzzer::MutateAndTestOne() /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:698:19
#4 0x45cc05 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:830:5
#5 0x44ac88 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:824:6
#6 0x474ab2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19:10
#7 0x7f4409b7a82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
SUMMARY: AddressSanitizer: heap-buffer-overflow /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3 in __asan_memcpy
Shadow bytes around the buggy address:
0x0c4a7fffb650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c4a7fffb660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c4a7fffb670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c4a7fffb680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c4a7fffb690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c4a7fffb6a0: 00 00 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==12==ABORTING

View File

@ -0,0 +1,94 @@
2020-02-05 17:50:20,470 - root - INFO - Fuzzer dex_fuzzer, detected error: INFO: Seed: 1337
INFO: Loaded 1 modules (8322 inline 8-bit counters): 8322 [0x8eaaf0, 0x8ecb72),
INFO: Loaded 1 PC tables (8322 PCs): 8322 [0x661bd8,0x6823f8),
INFO: 7 files found in /tmp/dex_fuzzer_corpus
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 46328 bytes
INFO: seed corpus: files: 7 min: 1264b max: 46328b total: 136996b rss: 36Mb
#8 INITED cov: 265 ft: 523 corp: 7/133Kb exec/s: 0 rss: 43Mb
#10 NEW cov: 266 ft: 538 corp: 8/147Kb lim: 46328 exec/s: 0 rss: 44Mb L: 13929/46328 MS: 2 ChangeBit-InsertByte-
#12 NEW cov: 270 ft: 550 corp: 9/192Kb lim: 46328 exec/s: 0 rss: 44Mb L: 46328/46328 MS: 2 ShuffleBytes-CrossOver-
#14 NEW cov: 270 ft: 557 corp: 10/201Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9103/46328 MS: 2 ChangeASCIIInt-EraseBytes-
#15 NEW cov: 270 ft: 565 corp: 11/218Kb lim: 46328 exec/s: 0 rss: 45Mb L: 16991/46328 MS: 1 InsertRepeatedBytes-
#21 NEW cov: 272 ft: 568 corp: 12/227Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9132/46328 MS: 1 InsertRepeatedBytes-
#25 NEW cov: 275 ft: 575 corp: 13/236Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9262/46328 MS: 4 ChangeBinInt-InsertByte-ChangeBit-EraseBytes-
#26 NEW cov: 280 ft: 602 corp: 14/244Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9133/46328 MS: 1 InsertByte-
#44 NEW cov: 280 ft: 609 corp: 15/254Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9290/46328 MS: 3 ChangeByte-InsertRepeatedBytes-InsertRepeatedBytes-
#60 NEW cov: 280 ft: 611 corp: 16/263Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9291/46328 MS: 1 InsertByte-
#67 NEW cov: 280 ft: 614 corp: 17/272Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9074/46328 MS: 2 CopyPart-EraseBytes-
#68 NEW cov: 280 ft: 617 corp: 18/305Kb lim: 46328 exec/s: 0 rss: 45Mb L: 34216/46328 MS: 1 InsertRepeatedBytes-
#70 NEW cov: 280 ft: 618 corp: 19/321Kb lim: 46328 exec/s: 0 rss: 45Mb L: 16543/46328 MS: 2 ChangeASCIIInt-CopyPart-
#71 NEW cov: 280 ft: 619 corp: 20/336Kb lim: 46328 exec/s: 0 rss: 45Mb L: 14936/46328 MS: 1 CopyPart-
#72 NEW cov: 280 ft: 623 corp: 21/345Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9255/46328 MS: 1 EraseBytes-
#79 NEW cov: 280 ft: 628 corp: 22/354Kb lim: 46328 exec/s: 0 rss: 45Mb L: 9263/46328 MS: 2 CopyPart-CMP- DE: "\x00\x00\x00\x00\x00\x00$'"-
#80 NEW cov: 280 ft: 632 corp: 23/363Kb lim: 46328 exec/s: 80 rss: 45Mb L: 9312/46328 MS: 1 InsertRepeatedBytes-
=================================================================
==12==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62500001b530 at pc 0x00000052138a bp 0x7ffe62db2c10 sp 0x7ffe62db23d8
READ of size 52 at 0x62500001b530 thread T0
SCARINESS: 26 (multi-byte-read-heap-buffer-overflow)
#0 0x521389 in __asan_memcpy /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
#1 0x567590 in yr_object_set_string /src/yara/libyara/object.c:1122:5
#2 0x5afced in dex_parse /src/yara/libyara/modules/dex/dex.c:781:5
#3 0x5b4a8b in dex__load /src/yara/libyara/modules/dex/dex.c:1218:7
#4 0x56537c in yr_modules_load /src/yara/libyara/modules.c:179:16
#5 0x5d6583 in yr_execute_code /src/yara/libyara/exec.c:1276:18
#6 0x56f5c0 in yr_scanner_scan_mem_blocks /src/yara/libyara/scanner.c:444:3
#7 0x56bf23 in yr_rules_scan_mem_blocks /src/yara/libyara/rules.c:235:12
#8 0x56c182 in yr_rules_scan_mem /src/yara/libyara/rules.c:285:10
#9 0x5548d2 in LLVMFuzzerTestOneInput /src/yara/tests/oss-fuzz/dex_fuzzer.cc:40:3
#10 0x45a3b1 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:556:15
#11 0x459ad5 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:470:3
#12 0x45be77 in fuzzer::Fuzzer::MutateAndTestOne() /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:698:19
#13 0x45cc05 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:830:5
#14 0x44ac88 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:824:6
#15 0x474ab2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19:10
#16 0x7f4409b7a82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#17 0x41e348 in _start (out/dex_fuzzer+0x41e348)
0x62500001b530 is located 0 bytes to the right of 9264-byte region [0x625000019100,0x62500001b530)
allocated by thread T0 here:
#0 0x521f4d in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:145:3
#1 0x4331b7 in operator new(unsigned long) (out/dex_fuzzer+0x4331b7)
#2 0x459ad5 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:470:3
#3 0x45be77 in fuzzer::Fuzzer::MutateAndTestOne() /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:698:19
#4 0x45cc05 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:830:5
#5 0x44ac88 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:824:6
#6 0x474ab2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19:10
#7 0x7f4409b7a82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
SUMMARY: AddressSanitizer: heap-buffer-overflow /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3 in __asan_memcpy
Shadow bytes around the buggy address:
0x0c4a7fffb650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c4a7fffb660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c4a7fffb670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c4a7fffb680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c4a7fffb690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c4a7fffb6a0: 00 00 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4a7fffb6f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==12==ABORTING
MS: 1 InsertByte-; base unit: e91e5af87998bbf9aa64218be0652d64ba4ea89d
artifact_prefix='./'; Test unit written to ./crash-ad6700613693ef977ff3a8c8f4dae239c3dde6f5