Skip to content

--oom-fuzz reported fusil's own bomb as a CPython contract violation - #273

Merged
devdanzin merged 1 commit into
mainfrom
oom-systemerror-marker-fires-on-fusils-own-bomb
Sep 5, 2026
Merged

--oom-fuzz reported fusil's own bomb as a CPython contract violation#273
devdanzin merged 1 commit into
mainfrom
oom-systemerror-marker-fires-on-fusils-own-bomb

Conversation

@devdanzin

Copy link
Copy Markdown
Owner

What

oom_call's except SystemError exists to catch a PyCFunction contract violation under allocation failure. But SystemError is one of the twelve _BOMB_EXCEPTIONS, so every bomb built with exc=NoneFailingIterator, SuperBomb, DescriptorBomb, HiddenNameType, … — raises it about one firing in twelve, and the handler reported fusil's own object as a finding with no C code involved at all.

The marker printed only the label, never the exception, so the two were indistinguishable in a kept crash dir.

How much this cost

Found while triaging fusil-fleet-oom-01 (--oom-foreign, 12 instances, 68,463 sessions, 3.7 h):

dirs share
oomclean (SystemError marker) 410 74%
OOM-0037 (subinterp/structseq segv) 60 11%
OOM-0027 (PyStackRef_BoolCheck) 60 11%
everything else 21 4%

All 418 resolved labels in those 410 dirs had a random-exception bomb passed to the firing call.

Evidence

  • Control — the same handler around a pure-Python callee handed a bomb, no extension module anywhere: 15/200 markers, matching SystemError's 1-in-12 share of the bomb tuple.
  • Replay — 7 fleet dirs replayed under the real LD_PRELOAD shim, chosen to include the hardest residue (builtins.tuple, builtins.frozenset, re.Scanner, tabnanny.Whitespace, trace._Ignore, gzip.GzipFile). Every one was a bomb: SystemError('fusil iter bomb') from FailingIterator.__next__, or SystemError('fusil superbomb via __eq__').
  • After — replaying the session behind one of those kept dirs: 4 markers → 0.

The fix

Every bomb message starts with "fusil " (samples/bomb_objects.py), which is the discriminator. The repr is now printed so a genuine one stays triageable from stdout alone.

Removing SystemError from _BOMB_EXCEPTIONS would have been the wrong fix: a slot raising an unexpected SystemError is exactly the hostile input that finds C code mishandling it — the bug is reporting it as the target's.

Tests

The handler is emitted as source into every generated script, so the new tests exec the emitted definition and drive it rather than asserting on snapshot text: a bomb SystemError is suppressed, a target's is still reported with its repr, MemoryError is still swallowed, and one merely mentioning "fusil" further into the message still reports.

ruff check + ruff format --check clean; 1298 tests pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WPBSmN87d2BqUnrDbojUT1

`oom_call`'s `except SystemError` exists to catch a PyCFunction contract
violation under allocation failure. But `SystemError` is one of the twelve
`_BOMB_EXCEPTIONS`, so every bomb built with `exc=None` -- `FailingIterator`,
`SuperBomb`, `DescriptorBomb`, `HiddenNameType`, and the rest -- raises it about
one firing in twelve, and the handler dutifully reported fusil's own object as a
finding with no C code involved at all.

The marker printed only the label, never the exception, so the two were
indistinguishable in a kept crash dir. That is what let it go unnoticed: an
--oom-foreign fleet of 68,463 sessions kept 410 SystemError dirs -- 74% of its
entire output -- and all 418 of their resolved labels had a random-exception bomb
passed to the firing call. Seven dirs replayed under the real shim, chosen to
include the hardest residue cases (`builtins.tuple`, `builtins.frozenset`,
`re.Scanner`), and every one was a bomb: `SystemError('fusil iter bomb')` or
`SystemError('fusil superbomb via __eq__')`. A control -- the same handler around
a pure-Python callee handed a bomb, no extension module anywhere -- fires the
marker at 15/200, matching SystemError's 1-in-12 share of the bomb tuple.

Every bomb message starts with "fusil " (samples/bomb_objects.py), which is the
discriminator, and the repr is now printed so a genuine one stays triageable from
stdout alone. Replaying the session behind one of those kept dirs goes from 4
markers to 0. Removing SystemError from `_BOMB_EXCEPTIONS` would have been the
wrong fix: a slot raising an unexpected SystemError is exactly the hostile input
that finds C code mishandling it -- the bug is reporting it as the target's.

The handler is emitted as source into every generated script, so the new tests
exec the emitted definition and drive it rather than asserting on snapshot text:
a bomb SystemError is suppressed, a target's is still reported with its repr, and
one merely mentioning "fusil" further into the message still reports.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WPBSmN87d2BqUnrDbojUT1
@devdanzin
devdanzin merged commit cdc9cfd into main Sep 5, 2026
2 checks passed
@devdanzin
devdanzin deleted the oom-systemerror-marker-fires-on-fusils-own-bomb branch September 5, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant