diff options
-rw-r--r-- | infra/cifuzz/fuzz_target.py | 6 | ||||
-rw-r--r-- | infra/cifuzz/stack_parser.py | 86 | ||||
-rw-r--r-- | infra/cifuzz/stack_parser_test.py | 78 |
3 files changed, 3 insertions, 167 deletions
diff --git a/infra/cifuzz/fuzz_target.py b/infra/cifuzz/fuzz_target.py index d82e89fa..355d25a2 100644 --- a/infra/cifuzz/fuzz_target.py +++ b/infra/cifuzz/fuzz_target.py @@ -24,7 +24,6 @@ import clusterfuzz.fuzz import config_utils import logs -import stack_parser logs.init() @@ -124,8 +123,9 @@ class FuzzTarget: # pylint: disable=too-many-instance-attributes shutil.copy(crash.input_path, target_reproducer_path) bug_summary_artifact_path = target_reproducer_path + '.summary' - stack_parser.parse_fuzzer_output(crash.stacktrace, - bug_summary_artifact_path) + with open(bug_summary_artifact_path, 'w') as handle: + handle.write(crash.stacktrace) + return target_reproducer_path def prune(self): diff --git a/infra/cifuzz/stack_parser.py b/infra/cifuzz/stack_parser.py deleted file mode 100644 index b53f875f..00000000 --- a/infra/cifuzz/stack_parser.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Module for parsing stacks from fuzz targets.""" - -import logging - -# From clusterfuzz: src/python/crash_analysis/crash_analyzer.py -# Used to get the beginning of the stacktrace. -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 stacktrace. -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', -] - - -def parse_fuzzer_output(fuzzer_output, parsed_output_file_path): - """Parses the fuzzer output from a fuzz target binary. - - Args: - fuzzer_output: A fuzz target binary output string to be parsed. - parsed_output_file_path: The location to store the parsed output. - """ - # Get index of key file points. - begin_stack = None - for marker in STACKTRACE_TOOL_MARKERS: - marker_index = fuzzer_output.find(marker) - if marker_index != -1: - begin_stack = marker_index - break - - if begin_stack is None: - logging.error( - b'Could not find a begin stack marker (%s) in fuzzer output:\n%s', - STACKTRACE_TOOL_MARKERS, fuzzer_output) - return - - end_stack = None - for marker in STACKTRACE_END_MARKERS: - marker_index = fuzzer_output.find(marker) - if marker_index != -1: - end_stack = marker_index + len(marker) - break - - if end_stack is None: - logging.error( - b'Could not find an end stack marker (%s) in fuzzer output:\n%s', - STACKTRACE_END_MARKERS, fuzzer_output) - return - - summary_str = fuzzer_output[begin_stack:end_stack] - - # Write sections of fuzzer output to specific files. - with open(parsed_output_file_path, 'a') as summary_handle: - summary_handle.write(summary_str) diff --git a/infra/cifuzz/stack_parser_test.py b/infra/cifuzz/stack_parser_test.py deleted file mode 100644 index 5a631b42..00000000 --- a/infra/cifuzz/stack_parser_test.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright 2021 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Tests for stack_parser.""" -import os -import unittest -from unittest import mock - -import parameterized -from pyfakefs import fake_filesystem_unittest - -import stack_parser - -# NOTE: This integration test relies on -# https://github.com/google/oss-fuzz/tree/master/projects/example project. -EXAMPLE_PROJECT = 'example' - -# Location of data used for testing. -TEST_DATA_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'test_data') - - -class ParseOutputTest(fake_filesystem_unittest.TestCase): - """Tests parse_fuzzer_output.""" - - def setUp(self): - self.setUpPyfakefs() - self.maxDiff = None # pylint: disable=invalid-name - - @parameterized.parameterized.expand([('example_crash_fuzzer_output.txt', - 'example_crash_fuzzer_bug_summary.txt'), - ('msan_crash_fuzzer_output.txt', - 'msan_crash_fuzzer_bug_summary.txt')]) - def test_parse_valid_output(self, fuzzer_output_file, bug_summary_file): - """Checks that the parse fuzzer output can correctly parse output.""" - # Read the fuzzer output from disk. - fuzzer_output_path = os.path.join(TEST_DATA_PATH, fuzzer_output_file) - self.fs.add_real_file(fuzzer_output_path) - with open(fuzzer_output_path, 'r') as fuzzer_output_handle: - fuzzer_output = fuzzer_output_handle.read() - bug_summary_path = '/bug-summary.txt' - with mock.patch('logging.info') as mock_info: - stack_parser.parse_fuzzer_output(fuzzer_output, bug_summary_path) - mock_info.assert_not_called() - - with open(bug_summary_path) as bug_summary_handle: - bug_summary = bug_summary_handle.read() - - # Compare the bug to the expected one. - expected_bug_summary_path = os.path.join(TEST_DATA_PATH, bug_summary_file) - self.fs.add_real_file(expected_bug_summary_path) - with open(expected_bug_summary_path) as expected_bug_summary_handle: - expected_bug_summary = expected_bug_summary_handle.read() - - self.assertEqual(expected_bug_summary, bug_summary) - - def test_parse_invalid_output(self): - """Checks that no files are created when an invalid input was given.""" - artifact_path = '/bug-summary.txt' - with mock.patch('logging.error') as mock_error: - stack_parser.parse_fuzzer_output('not a valid output_string', - artifact_path) - assert mock_error.call_count - self.assertFalse(os.path.exists(artifact_path)) - - -if __name__ == '__main__': - unittest.main() |