From cf66ef99e4079c63bd0dc88f868030e02fee350a Mon Sep 17 00:00:00 2001 From: devdanzin <74280297+devdanzin@users.noreply.github.com> Date: Tue, 18 Aug 2026 18:05:11 -0300 Subject: [PATCH] blacklist the __pypy__ helpers that attack the fuzzer instead of the target Caught on the first PyPy 3.11 fleet config. Five members of __pypy__ are debug/test plumbing, not fuzzable surface, and each manufactures a crash that never happened: - attach_gdb: runs an interp-level gdb *inside the session*. Its banner ("For bug reporting instructions...") lands in the captured stdout and scores on the "bug" word. Observed live: session kept as `__pypy__-bug-systemerror`. - _internal_crash: documented as "for testing purposes, raise an interpreter-level ValueError. Should turn into a SystemError automatically". SystemError is a 1.0 crash word, so with --test-private this alone tagged 4 of 4 __pypy__ sessions as crashes. - remote_exec: "Executes a script of Python code in a given remote Python process" -- a fuzzer-chosen pid is arbitrary code injection into any process on the box, including sibling fleet instances and the fuzzer itself. - set_code_callback: process-global hook run on every code-object creation; handed one of fusil's bomb objects, the rest of the session detonates on unrelated code. - pyos_inputhook / revdb_stop: block. The entry is deliberately narrow -- __pypy__ is PyPy-only surface with no CPython counterpart, which is the reason to fuzz PyPy at all, so newdict/strategy/internal_repr/ intop/move_to_end and the rest stay fuzzable. A test pins both directions. Verified against PyPy 3.11.15 / 7.3.23: 5 of 5 __pypy__ sessions now come back clean (previously 4 of 4 false-tagged) with 3.3k-4.9k lines of real fuzzing each. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01WhcpLoyjUWLbETGZnA9boj --- fusil/python/blacklists.py | 25 +++++++++++++++++++++++++ tests/python/test_blacklists.py | 15 +++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/fusil/python/blacklists.py b/fusil/python/blacklists.py index 317f4cd..0b3acca 100644 --- a/fusil/python/blacklists.py +++ b/fusil/python/blacklists.py @@ -153,6 +153,31 @@ "sigwaitinfo", "sigtimedwait", }, + # PyPy interpreter internals (__pypy__) that attack the fuzzer or the host rather than + # the target -- the "generated code kills its own session" class (see fusil #192). + # Everything else in __pypy__ is deliberately left fuzzable: it is PyPy-only surface with + # no CPython counterpart, which is the whole point of fuzzing PyPy. + "__pypy__": { + # Spawns an interp-level gdb *inside the session*. Observed live: gdb's own banner + # ("For bug reporting instructions...") lands in the captured stdout and scores on + # the "bug" word, so the session is kept as a crash that never happened. + "attach_gdb", + # "Executes a script of Python code in a given remote Python process." A fuzzer-chosen + # pid means arbitrary code injection into any process on the box -- including sibling + # fleet instances and the fuzzer itself. + "remote_exec", + # Installs a process-global hook invoked on every code-object creation; handing it one + # of fusil's bomb objects makes the rest of the session detonate on unrelated code. + "set_code_callback", + # Blocks: calls PyOS_InputHook() / stops under the reverse debugger. + "pyos_inputhook", + "revdb_stop", + # Documented as "for testing purposes, raise an interpreter-level ValueError. + # Should turn into a SystemError automatically" -- a deliberate self-test helper. + # "SystemError" is a 1.0 crash word, so with --test-private this alone tagged + # EVERY __pypy__ session as a crash. Manufactured signal, never a target bug. + "_internal_crash", + }, "_socket": SOCKET, "socket": SOCKET, "posix": POSIX, diff --git a/tests/python/test_blacklists.py b/tests/python/test_blacklists.py index 8c62f28..486ca36 100644 --- a/tests/python/test_blacklists.py +++ b/tests/python/test_blacklists.py @@ -35,6 +35,21 @@ def test_module_class_keys_have_nonempty_parts(self): class TestKnownEntriesPresent(unittest.TestCase): """Pin a few high-value entries so accidental deletion is caught.""" + def test_pypy_self_harming_helpers_blacklisted(self): + # These attack the fuzzer or the host, not the target. attach_gdb was caught live on + # a PyPy 3.11 fleet: it runs gdb inside the session and gdb's banner scores on the + # "bug" word, manufacturing a crash. remote_exec injects code into an arbitrary pid. + self.assertLessEqual( + {"attach_gdb", "remote_exec", "set_code_callback", "_internal_crash"}, + bl.BLACKLIST["__pypy__"], + ) + + def test_pypy_blacklist_stays_narrow(self): + # __pypy__ is PyPy-only surface with no CPython counterpart -- the reason to fuzz PyPy + # at all. Only the self-harming helpers belong here, never the interesting internals. + for keep in ("newdict", "strategy", "internal_repr", "intop", "move_to_end"): + self.assertNotIn(keep, bl.BLACKLIST["__pypy__"]) + def test_sys_trace_hooks_blacklisted(self): self.assertEqual( bl.BLACKLIST["sys"] & {"settrace", "setprofile"}, {"settrace", "setprofile"}