generated code: keep source.py parseable/runnable on a pre-3.13 target (PyPy 3.11) - #256
Merged
Conversation
fusil's own floor is 3.13, but that constrains the *runner*. The generated script is
executed by --python, which is routinely older -- PyPy 3.11 is a fuzzing target in its
own right. Two constructs leaked into the emitted script and killed every session
before any fuzzing happened:
- `f"{expr!r }"` (a space between the conversion and the closing brace) is PEP 701
syntax, so the whole script is a SyntaxError on a 3.11 target. Three emitter sites in
write_python_code.py; tightening them to `{expr!r}` leaves the printed text identical.
- `types.CapsuleType` is 3.13+, read unguarded in the tricky-objects prelude. On PyPy
3.11 every session died at that line with AttributeError. Guarded the same way the
neighbouring `types.GenericAlias` already is; the `if tricky_capsule:` use site below
it already expects a falsy value.
Both failure modes are invisible from the outside: the run looks *clean* (no crashes
kept) because nothing ever ran. Measured on PyPy 3.11.15 / 7.3.23, a 12-session run went
from 0 fuzzed calls to thousands of lines of real activity per session.
tests/python/test_target_py311_compat.py locks both in (each fails if its fix is
reverted). Golden snapshot regenerated for the intentional output change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhcpLoyjUWLbETGZnA9boj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
fusil's
requires-pythonfloor of 3.13 constrains the runner. The generatedsource.pyis executed by--python, which is routinely older or alternative — PyPy 3.11is a fuzzing target in its own right.
Two 3.12+/3.13+ constructs leaked into the emitted script and aborted every session
before any fuzzing happened:
f"{expr!r }"— a space between the conversion and the closing brace is PEP 701syntax (3.12+). On a 3.11 target the entire script is a
SyntaxError, so nothingparses. Three emitter sites in
write_python_code.py; tightening them to{expr!r}leaves the printed text byte-identical.
types.CapsuleType— 3.13+, read unguarded in the tricky-objects prelude. On PyPy3.11 every session died at that line with
AttributeError. Now guarded exactly the waythe neighbouring
types.GenericAliasalready is; theif tricky_capsule:use siteimmediately below already expects a falsy value.
Why this is worth a test
Both failure modes are invisible from the outside: the run looks clean — no crashes,
no kept dirs — because nothing ever ran. A 12-session PyPy run reported zero findings while
in fact zero calls had been fuzzed.
Measured on PyPy 3.11.15 / 7.3.23 (
--discover-in-target, CPython 3.14 runner): sessionswent from dying in the prelude to thousands of lines of real activity each.
Tests
tests/python/test_target_py311_compat.py— verified each test fails when its fix isreverted:
typesshim withCapsuleTyperemoved, assertingit survives and yields
None;fusil/for a conversion field followed by whitespace, andchecks the golden snapshot for the same.
Full suite green (1247 tests);
ruff check+ruff format --checkclean. Golden snapshotregenerated for the intentional output change.
🤖 Generated with Claude Code