Skip to content

Commit 9c6c1ff

Browse files
Reduce bug filing threshold for libfuzzer-based Android crashes (#4474)
Updated triage.py to file bugs for fuzzer crashes by lowering the threshold limit. These fuzzers run on Android platform and on libfuzzer engine.
1 parent 777669e commit 9c6c1ff

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

src/clusterfuzz/_internal/cron/triage.py

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,60 @@ def _is_bug_filed(testcase):
9898
return False
9999

100100

101+
def _is_blocking_progress_android(testcase):
102+
"""Checks the crash frequency if it is reported on libfuzzer"""
103+
if testcase.job_type.startswith('libfuzzer'):
104+
# Get crash statistics data on this unreproducible crash for last X days.
105+
last_hour = crash_stats.get_last_successful_hour()
106+
if not last_hour:
107+
# No crash stats available, skip.
108+
return False
109+
110+
_, rows = crash_stats.get(
111+
end=last_hour,
112+
block='day',
113+
days=data_types.FILE_CONSISTENT_UNREPRODUCIBLE_TESTCASE_DEADLINE,
114+
group_by='reproducible_flag',
115+
where_clause=(
116+
'crash_type = %s AND crash_state = %s AND security_flag = %s' %
117+
(json.dumps(testcase.crash_type), json.dumps(testcase.crash_state),
118+
json.dumps(testcase.security_flag))),
119+
group_having_clause='',
120+
sort_by='total_count',
121+
offset=0,
122+
limit=1)
123+
124+
# Calculate total crash count and crash days count.
125+
crash_days_indices = set()
126+
total_crash_count = 0
127+
for row in rows:
128+
if 'groups' not in row:
129+
continue
130+
131+
total_crash_count += row['totalCount']
132+
for group in row['groups']:
133+
for index in group['indices']:
134+
crash_days_indices.add(index['hour'])
135+
136+
crash_days_count = len(crash_days_indices)
137+
# Considers an unreproducible testcase as important if the crash
138+
# occurred at least once everyday for the last 14 days and total
139+
# crash count exceeded 14.
140+
return (crash_days_count ==
141+
data_types.FILE_CONSISTENT_UNREPRODUCIBLE_TESTCASE_DEADLINE and
142+
total_crash_count >=
143+
data_types.FILE_UNREPRODUCIBLE_TESTCASE_MIN_STARTUP_CRASH_THRESHOLD)
144+
145+
return False
146+
147+
148+
def is_crash_important_android(testcase):
149+
""""Indicate if the android crash is important to file."""
150+
if _is_blocking_progress_android(testcase):
151+
return True
152+
return False
153+
154+
101155
def _is_crash_important(testcase):
102156
"""Indicate if the crash is important to file."""
103157
if not testcase.one_time_crasher_flag:
@@ -429,10 +483,19 @@ def main():
429483
# Check if the crash is important, i.e. it is either a reproducible crash
430484
# or an unreproducible crash happening frequently.
431485
if not _is_crash_important(testcase):
432-
_set_testcase_stuck_state(testcase, False)
433-
logs.info(
434-
f'Skipping testcase {testcase_id}, since the crash is not important.')
435-
continue
486+
# Check if the crash is a startup crash, i.e. it is causing the fuzzer
487+
# to crash on startup and not allowing the fuzzer to run longer
488+
if testcase.platform == "android" and is_crash_important_android(
489+
testcase):
490+
logs.info(
491+
f'Considering testcase {testcase_id}, since it is a startup crash'
492+
' on android platform.')
493+
else:
494+
_set_testcase_stuck_state(testcase, False)
495+
logs.info(
496+
f'Skipping testcase {testcase_id}, since the crash is not important.'
497+
)
498+
continue
436499

437500
# Require that all tasks like minimizaton, regression testing, etc have
438501
# finished.

src/clusterfuzz/_internal/datastore/data_types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@
5858
# Minimum number of unreproducible crashes to see before filing it.
5959
FILE_UNREPRODUCIBLE_TESTCASE_MIN_CRASH_THRESHOLD = 100
6060

61+
# Minimum number of unreproducible crashes to see before filing it for android.
62+
FILE_UNREPRODUCIBLE_TESTCASE_MIN_STARTUP_CRASH_THRESHOLD = 14
63+
6164
# Heartbeat wait interval.
6265
HEARTBEAT_WAIT_INTERVAL = 10 * 60
6366

src/clusterfuzz/_internal/tests/appengine/handlers/cron/triage_test.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,67 @@
2525
from clusterfuzz._internal.tests.test_libs import test_utils
2626

2727

28+
@test_utils.with_cloud_emulators('datastore')
29+
class AndroidCrashImportantTest(unittest.TestCase):
30+
"""Tests for is_crash_important_android."""
31+
32+
def setUp(self):
33+
helpers.patch(self, [
34+
'clusterfuzz._internal.metrics.crash_stats.get_last_successful_hour',
35+
'clusterfuzz._internal.metrics.crash_stats.get',
36+
'clusterfuzz._internal.base.utils.utcnow',
37+
])
38+
self.mock.utcnow.return_value = test_utils.CURRENT_TIME
39+
40+
def test_is_crash_important_android_1(self):
41+
"""If this unreproducible testcase (libfuzzer) is crashing frequently,
42+
then it is an important crash."""
43+
self.mock.get_last_successful_hour.return_value = 417325
44+
indices = [{
45+
'count': 1,
46+
'hour': day_index
47+
} for day_index in range(417325, 416989, -24)]
48+
49+
self.mock.get.return_value = (1, [{
50+
'totalCount': 14,
51+
'groups': [{
52+
'indices': indices,
53+
'name': 'false',
54+
},]
55+
}])
56+
testcase = test_utils.create_generic_testcase()
57+
testcase.job_type = 'libfuzzer_test'
58+
testcase.platform = 'android'
59+
testcase.one_time_crasher_flag = True
60+
testcase.put()
61+
62+
self.assertTrue(triage.is_crash_important_android(testcase))
63+
64+
def test_is_crash_important_android_2(self):
65+
"""If this unreproducible testcase (libfuzzer) is less than the
66+
total crash threshold, then it is not important."""
67+
self.mock.get_last_successful_hour.return_value = 417325
68+
indices = [{
69+
'count': day_index % 5 == 0,
70+
'hour': day_index
71+
} for day_index in range(417325, 416989, -24)]
72+
73+
self.mock.get.return_value = (1, [{
74+
'totalCount': 3,
75+
'groups': [{
76+
'indices': indices,
77+
'name': 'false',
78+
},]
79+
}])
80+
testcase = test_utils.create_generic_testcase()
81+
testcase.job_type = 'libfuzzer_test'
82+
testcase.platform = 'android'
83+
testcase.one_time_crasher_flag = True
84+
testcase.put()
85+
86+
self.assertFalse(triage.is_crash_important_android(testcase))
87+
88+
2889
@test_utils.with_cloud_emulators('datastore')
2990
class CrashImportantTest(unittest.TestCase):
3091
"""Tests for _is_crash_important."""

0 commit comments

Comments
 (0)