Skip to content

Commit 66f2069

Browse files
Prevent accidental DoS on malformed stacktraces (#3980)
Enormous stacktraces containing a giant array on a single line is causing bots to freeze. Although fuzztest really should not be printing an input this large, let's try to be resilient when it misbehaves. Fixes: #3978
1 parent db23f2c commit 66f2069

File tree

3 files changed

+18
-2
lines changed

3 files changed

+18
-2
lines changed

src/clusterfuzz/_internal/tests/core/crash_analysis/stack_parsing/stack_analyzer_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3744,6 +3744,12 @@ def test_split_stacktrace(self):
37443744

37453745
self.assertEqual(actual_split_stacktrace, expected_split_stacktrace)
37463746

3747+
def test_split_stacktrace_no_dos(self):
3748+
"""Tests that split_stacktrace wont search forever unless there's a very
3749+
well crafted input."""
3750+
stacktrace = "S text\n" + " 42 " * 100000 + "j text\n"
3751+
StackParser.split_stacktrace(stacktrace)
3752+
37473753
def test_jazzer_js_javascript(self):
37483754
"""Test Jazzer.js JS stacktrace."""
37493755
data = self._read_test_data('jazzer_js_javascript.txt')

src/clusterfuzz/stacktraces/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,10 @@ def should_ignore_line_for_crash_processing(line, state):
14071407
if SAN_DEADLYSIGNAL_REGEX.match(line):
14081408
return True
14091409

1410+
if len(line) > 1024**2:
1411+
logs.log_error('Line is too long for a stacktrace.')
1412+
return True
1413+
14101414
return False
14111415

14121416

src/clusterfuzz/stacktraces/constants.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,11 @@
6666
# The 'assertion .* failed' is suffixed with the failure reason. E.g.,
6767
# 'assertion __dest < len() failed: sample_array[] index out of bounds',
6868
# 'assertion !full() failed: sample_function() called on an empty vector'
69+
# Add the lookahead group to prevent DoS, see
70+
# https://github.com/google/clusterfuzz/issues/3978.
6971
ASSERT_REGEX_GLIBC_SUFFIXED = re.compile(
70-
r'.*\S.*\/.*:\d+:\s*assertion .* failed:\s*(\S.*)')
72+
r'(?=.*assertion .* failed:.).*.*\S.*\/.*:\d+:\s*assertion .* failed:\s*(\S.*)' # pylint: disable=line-too-long
73+
)
7174
ASSERT_NOT_REACHED_REGEX = re.compile(r'^\s*SHOULD NEVER BE REACHED\s*$')
7275
CENTIPEDE_TIMEOUT_REGEX = re.compile(r'(?:%s)' % '|'.join([
7376
r'========= Timeout of \d+ seconds exceeded; exiting',
@@ -200,8 +203,11 @@
200203
r'[ ]*([^ ]*|Atomic [^ ]*) of size ([^ ]*) at ([^ ]*)')
201204
SAN_DEADLYSIGNAL_REGEX = re.compile(
202205
r'(Address|Leak|Memory|UndefinedBehavior|Thread)Sanitizer:DEADLYSIGNAL')
206+
# Use the lookahead group to prevent DoS, see
207+
# https://github.com/google/clusterfuzz/issues/3978.
203208
CONCATENATED_SAN_DEADLYSIGNAL_REGEX = re.compile(
204-
r'\n([^\n]*\S[^\n]*)(' + SAN_DEADLYSIGNAL_REGEX.pattern + r')\n')
209+
r'\n(?=.*Sanitizer\:DEADLYSIGNAL.*)([^\n]*\S[^\n]*)(' +
210+
SAN_DEADLYSIGNAL_REGEX.pattern + r')\n')
205211
SPLIT_CONCATENATED_SAN_DEADLYSIGNAL_REGEX = r'\n\1\n\2\n'
206212
SAN_FPE_REGEX = re.compile(r'.*[a-zA-Z]+Sanitizer: FPE ')
207213
SAN_ILL_REGEX = re.compile(r'.*[a-zA-Z]+Sanitizer: ILL ')

0 commit comments

Comments
 (0)