Skip to content

python: emit more robust generated code to avoid self-inflicted crashes - #192

Merged
devdanzin merged 1 commit into
mainfrom
robust-generated-code-avoid-self-crashes
Jul 5, 2026
Merged

python: emit more robust generated code to avoid self-inflicted crashes#192
devdanzin merged 1 commit into
mainfrom
robust-generated-code-avoid-self-crashes

Conversation

@devdanzin

Copy link
Copy Markdown
Owner

Problem

Triaging a cereggii fleet, the non-target noise (after the SystemError-ignore change) was dominated by sessions the generated script kills itself in, not real target crashes: ~100 sigint and ~148 exitcode1. All are avoidable by emitting more robust code.

Fixes

1. sigint — bomb objects drawing KeyboardInterrupt. _BOMB_EXCEPTIONS included KeyboardInterrupt, a BaseException subclass. The generated call sites catch except Exception, so a bomb that draws it escapes and aborts the whole session (SIGINT) as a false crash. Dropped it — the pool is now Exception-only (bombs exist to exercise error paths, not to kill the interpreter). +regression test.

2. exitcode1 (AttributeError subset) — random module shadowed. The generated boilerplate did from random import …, random, …, rebinding the bare name random to the random() function and shadowing the random module that embedded tricky-object code imports and calls as random.randint(...)AttributeError at module load. The name is never used bare in generated scripts, so we stop importing it; random now reliably resolves to the module. (Complements the existing _bomb_random alias that already hardened the core bomb source.)

3. exitcode1 (the rest) — unprotected argument construction. A call's arguments are constructed inline, before callMethod/callFunc runs — so a hostile literal argument (a set containing an unhashable object, a dict keyed on a __hash__ that raises, a bomb object) raises during argument construction, which callMethod's own handler can't catch, and escapes at module level. Wrap the whole res_X = callMethod(...) statement in try/except so the call is skipped and fuzzing continues. The thread/async call wrappers already protect their own argument construction; this closes the synchronous path.

Impact / validation

In the sampled fleet, every exitcode1 was a res_X = callMethod/callFunc(...) whose argument construction raised, and every sigint was a bomb KeyboardInterrupt — all now avoided. All three fixes verified at runtime; golden output regenerated and validated as parseable Python.

Tests

  • New TestBombExceptionPool (pool stays Exception-only; _bomb_exc() never returns a BaseException-only type).
  • Golden fakemod_seed1234.py regenerated (locks in the call wrapping + the random import).
  • Full suite green (1038); ruff check + ruff format --check clean.

🤖 Generated with Claude Code

…hes"

Triaging a cereggii fleet, the non-target noise was dominated by sessions the
generated script kills itself in, not real target crashes. Three fixes remove them:

1. sigint (~all of them): _BOMB_EXCEPTIONS included KeyboardInterrupt, a
   BaseException subclass. The generated call sites catch `except Exception`, so a
   bomb that draws KeyboardInterrupt escapes and aborts the whole session (SIGINT).
   Drop it -- the pool is now Exception-only (bombs exist to exercise error paths,
   not to kill the interpreter). +regression test that the pool stays Exception-only.

2. exitcode1 (the AttributeError subset): the generated boilerplate did
   `from random import ..., random, ...`, rebinding the bare name `random` to the
   random() *function* and shadowing the module that embedded tricky-object code
   imports and calls as `random.randint(...)` -> AttributeError at module load. The
   name is never used bare in generated scripts, so just stop importing it; `random`
   now reliably resolves to the module. (Complements the existing `_bomb_random`
   alias that already hardened the core bomb source.)

3. exitcode1 (the rest): a call's arguments are constructed inline, BEFORE
   callMethod/callFunc runs -- so a hostile literal argument (a set of an unhashable
   object, a dict keyed on a __hash__ that raises, a bomb object) raises during
   argument construction, which callMethod's own handler cannot catch, and escapes
   at module level. Wrap the whole `res_X = callMethod(...)` statement in
   try/except so the call is skipped and fuzzing continues. The thread/async call
   wrappers already protect their own argument construction; this closes the
   synchronous path. Golden regenerated.

Every exitcode1 in the sampled fleet was one of these; every sigint was a bomb
KeyboardInterrupt. Suite green (1038), golden output validated as parseable Python.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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