Skip to content

Commit f50c6ce

Browse files
[Fix] Fix find testcase when dedup only same target is set (#5021)
As described in b/459451185#comment2, there is a failure state where minimize keeps failing with an exception when the `DEDUP_ONLY_SAME_TARGET` env is set for a job. This is happening due to two codepaths that were fixed here: * Minimimize calls `data_handler.handle_duplicate_entry()` which uses the `testcase.fuzzer_name` instead of the `testcase.actual_fuzzer_name()` for the `fuzz_target`. * `data_handler.find_testcase()` had a misplaced assert that would fail if the first engine from the list was not the one used by the testcase. Also, note that this PR changes the behavior to avoid the assert. Instead, we log a more descriptive error and return None, since failing to parse the fuzz_target is an expected issue (could be result of misconfiguration) and should be handled gracefully. b/459451185
1 parent 2bbb486 commit f50c6ce

File tree

2 files changed

+90
-3
lines changed

2 files changed

+90
-3
lines changed

src/clusterfuzz/_internal/datastore/data_handler.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,13 @@ def find_testcase(project_name,
159159
culprit_engine = engine
160160
target_without_engine = fuzz_target[len(culprit_engine) + 1:]
161161
break
162-
target_with_different_engines = []
163-
assert culprit_engine
162+
163+
if not culprit_engine:
164+
logs.error(
165+
f'Engine not found for target {fuzz_target} when searching for a '
166+
'testcase. If the job sets DEDUP_ONLY_SAME_TARGET, the fuzz target '
167+
'is expected to follow the pattern: <engine>_<target>.')
168+
return None
164169

165170
target_with_different_engines = [
166171
f'{engine}_{target_without_engine}' for engine in fuzzing.ENGINES
@@ -661,7 +666,7 @@ def handle_duplicate_entry(testcase):
661666
testcase.crash_state,
662667
testcase.security_flag,
663668
testcase_to_exclude=testcase,
664-
fuzz_target=testcase.fuzzer_name)
669+
fuzz_target=testcase.actual_fuzzer_name())
665670
if not existing_testcase:
666671
return
667672

src/clusterfuzz/_internal/tests/core/datastore/data_handler_test.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,88 @@ def test_find_testcase(self):
288288
self.assertTrue(
289289
test_utils.entities_equal(exclude_result, reproducible_no_bug))
290290

291+
@parameterized.parameterized.expand([
292+
('engine1_target_test1', True), # Same fuzz_target.
293+
('engine2_target_test1', True), # Same fuzz_target with diff engine.
294+
('engine1_target_test3', False) # Not matching any fuzz_target.
295+
])
296+
@mock.patch('clusterfuzz._internal.fuzzing.ENGINES', ['engine1', 'engine2'])
297+
def test_find_testcase_with_fuzz_target_deduplication(self, fuzz_target,
298+
should_find):
299+
"""Test find_testcase with fuzz_target deduplication enabled."""
300+
environment.set_value('DEDUP_ONLY_SAME_TARGET', True)
301+
302+
# Create testcases with different fuzz targets.
303+
testcase1 = test_utils.create_generic_testcase()
304+
testcase1.one_time_crasher_flag = False # higher priority
305+
testcase1.project_name = 'project'
306+
testcase1.crash_type = 'type'
307+
testcase1.crash_state = 'state'
308+
testcase1.security_flag = True
309+
testcase1.overridden_fuzzer_name = 'engine1_target_test1'
310+
testcase1.fuzzer_name = 'engine1'
311+
testcase1.status = 'Processed'
312+
testcase1.open = True
313+
testcase1.put()
314+
315+
testcase2 = test_utils.create_generic_testcase()
316+
testcase1.one_time_crasher_flag = True # lower priority
317+
testcase2.project_name = 'project'
318+
testcase2.crash_type = 'type'
319+
testcase2.crash_state = 'state'
320+
testcase2.security_flag = True
321+
testcase2.overridden_fuzzer_name = 'engine2_target_test1'
322+
testcase2.fuzzer_name = 'engine2'
323+
testcase2.status = 'Processed'
324+
testcase2.open = True
325+
testcase2.put()
326+
327+
testcase3 = test_utils.create_generic_testcase()
328+
testcase3.one_time_crasher_flag = False
329+
testcase3.project_name = 'project'
330+
testcase3.crash_type = 'type'
331+
testcase3.crash_state = 'state'
332+
testcase3.security_flag = True
333+
testcase3.overridden_fuzzer_name = 'engine1_target_test2'
334+
testcase3.fuzzer_name = 'engine1'
335+
testcase3.status = 'Processed'
336+
testcase3.open = True
337+
testcase3.put()
338+
339+
result = data_handler.find_testcase(
340+
'project', 'type', 'state', True, fuzz_target=fuzz_target)
341+
if should_find:
342+
# Uses testcase1 as it has the highest priority.
343+
self.assertTrue(test_utils.entities_equal(result, testcase1))
344+
else:
345+
self.assertIsNone(result)
346+
347+
@parameterized.parameterized.expand([
348+
'unknown_target_test2', # Engine in fuzz_target not found.
349+
'engine1' # fuzz_target malformatted.
350+
])
351+
@mock.patch('clusterfuzz._internal.fuzzing.ENGINES', ['engine1', 'engine2'])
352+
def test_find_testcase_with_fuzz_target_deduplication_exceptions(
353+
self, fuzz_target):
354+
"""Test handled failures in find_testcase with fuzz_target dedup enabled."""
355+
environment.set_value('DEDUP_ONLY_SAME_TARGET', True)
356+
357+
testcase = test_utils.create_generic_testcase()
358+
testcase.one_time_crasher_flag = True
359+
testcase.project_name = 'project'
360+
testcase.crash_type = 'type'
361+
testcase.crash_state = 'state'
362+
testcase.security_flag = True
363+
testcase.overridden_fuzzer_name = 'engine1_target_test2'
364+
testcase.fuzzer_name = 'engine1'
365+
testcase.status = 'Processed'
366+
testcase.open = True
367+
testcase.put()
368+
369+
result = data_handler.find_testcase(
370+
'project', 'type', 'state', True, fuzz_target=fuzz_target)
371+
self.assertIsNone(result)
372+
291373
def test_get_issue_description_oom(self):
292374
"""Test get_issue_description for an oom testcase."""
293375
self.mock.get().name = 'chromium'

0 commit comments

Comments
 (0)