diff --git a/fusil/python/__init__.py b/fusil/python/__init__.py index a943ece..09d4407 100644 --- a/fusil/python/__init__.py +++ b/fusil/python/__init__.py @@ -982,6 +982,12 @@ def setupProject(self) -> None: # single PyPy fleet. r"fusil (bomb|iter bomb|superbomb|fileno bomb|hidden name|descriptor (get|set)" r"|stateful hash|instancecheck|junk return|monitoring callback bomb)", + # http.cookiejar warns "http.cookiejar bug!" (its own words) when it meets a + # malformed cookie -- routine for a fuzzer. The message contains the "bug" word + # (0.10), so on its own it is harmless, but combined with another weak signal it + # pushes a boring session over the threshold: 6 kept dirs in one PyPy fleet. It + # is the target's benign diagnostic, not a target defect. + r"http\.cookiejar bug!", # The --new-uninit region prints a progress marker per poked type, # e.g. "[NEW-UNINIT] poking SystemError". The type name is arbitrary and # routinely collides with a crash word ("SystemError" -> a 1.0 hit) or, worse, diff --git a/fusil/python/blacklists.py b/fusil/python/blacklists.py index e2937bb..42cc387 100644 --- a/fusil/python/blacklists.py +++ b/fusil/python/blacklists.py @@ -191,6 +191,13 @@ # _testmultiphase (foo/Example/Str) is real multi-phase-init surface worth fuzzing, and on # PyPy it exercises the cpyext C-API emulation layer. "_testmultiphase": {"call_state_registration_func"}, + # asyncio.runners.Runner._on_sigint is the SIGINT handler the Runner installs; called + # directly as a fuzz target it unconditionally `raise KeyboardInterrupt()`. Like + # signal.default_int_handler, that is a BaseException, so it escapes the generated + # script's `except Exception` handlers and kills the session (the #192 class). It was + # 29 of 53 kept dirs -- 55% -- in one PyPy fleet, reached because --test-private exposes + # the underscore-prefixed method. + "asyncio.runners:Runner": {"_on_sigint"}, "_socket": SOCKET, "socket": SOCKET, "posix": POSIX, diff --git a/tests/python/test_blacklists.py b/tests/python/test_blacklists.py index 966a633..7c66835 100644 --- a/tests/python/test_blacklists.py +++ b/tests/python/test_blacklists.py @@ -44,6 +44,12 @@ def test_testmultiphase_state_func_blacklisted_but_module_is_not(self): for keep in ("foo", "Example", "Str"): self.assertNotIn(keep, bl.BLACKLIST["_testmultiphase"]) + def test_asyncio_runner_on_sigint_blacklisted(self): + # Same class as default_int_handler: it raises KeyboardInterrupt, a BaseException, + # which escapes the generated script's handlers and kills the session. Reached only + # because --test-private exposes the underscore-prefixed method. + self.assertIn("_on_sigint", bl.BLACKLIST["asyncio.runners:Runner"]) + def test_default_int_handler_blacklisted(self): # It raises KeyboardInterrupt, a BaseException, which escapes the generated script's # `except Exception` handlers and kills the session outright (the #192 class). diff --git a/tests/test_file_watch.py b/tests/test_file_watch.py index 0ee6f77..d2fe223 100644 --- a/tests/test_file_watch.py +++ b/tests/test_file_watch.py @@ -287,3 +287,20 @@ def test_ignore_regex_covers_every_raised_bomb_signature(self): "fusil/python/__init__.py, so they will be scored as target crashes: " + ", ".join(uncovered), ) + + +class TestCookiejarWarningIgnored(unittest.TestCase): + """http.cookiejar's own "bug!" warning must not push a boring session over the threshold. + + The message is the target's benign diagnostic for a malformed cookie -- routine input for + a fuzzer -- but it contains the "bug" word (0.10). On its own that is harmless; combined + with another weak signal it kept 6 dirs in one PyPy fleet. + """ + + def test_cookiejar_warning_is_ignored_but_a_real_hit_still_scores(self): + w = _watch(words={"bug": 0.10, "segfault": 1.0}) + w.ignoreRegex(r"http\.cookiejar bug!") + self.assertIsNone(w.processLine(b"x.py:1369: UserWarning: http.cookiejar bug!")) + self.assertEqual(w.score, 0.0) + w.processLine(b"got a segfault here") + self.assertEqual(w.score, 1.0)