Skip to content

gh-151763: Fix OOM-0013 crash when the parser or compiler fails to allocate#151968

Merged
pablogsal merged 2 commits into
python:mainfrom
tonghuaroot:gh-151763-oom-contract
Jul 2, 2026
Merged

gh-151763: Fix OOM-0013 crash when the parser or compiler fails to allocate#151968
pablogsal merged 2 commits into
python:mainfrom
tonghuaroot:gh-151763-oom-contract

Conversation

@tonghuaroot

Copy link
Copy Markdown
Contributor

This fixes OOM-0013 from the gh-151763 umbrella.

compile(), exec(), eval() and ast.parse() can return NULL without
setting an exception when an allocation fails, breaking the result/error
contract every consumer relies on and crashing on the assertion that checks it
(for example the (res != NULL) ^ (PyErr_Occurred()) check reached from the
evaluation loop, and assert(!PyErr_Occurred()) at the top of
_PyAST_Validate).

Three points returned NULL silently under a failed allocation:

  • new_compiler() returned NULL without calling PyErr_NoMemory() when its
    initial PyMem_Calloc failed -- the dominant compile()/exec()/eval()
    path.
  • _PyPegen_run_parser() could return a valid result while leaving a stale
    exception pending.
  • The tokenizer-init helpers could fail silently.

Setting the error indicator at each point makes a failed allocation surface as
MemoryError instead of a NULL-without-exception crash.

Reproduction (debug build, _testcapi.set_nomemory)

  • ast.parse(...) under set_nomemory aborted with "a function returned NULL
    without setting an exception"; a clean MemoryError is raised after the fix.
  • compile() / exec() / eval() under set_nomemory aborted at the
    result/error contract assertion; clean after the fix.
  • Reverting either guard reproduces the corresponding abort (negative control).

No test is added, following the convention for these OOM crash fixes.

… to allocate

compile(), exec(), eval() and ast.parse() could return NULL without an
exception set when an allocation failed, breaking the result/error contract
and crashing on the assertion that checks it.  Set the error indicator at the
points that returned NULL silently: new_compiler(), the _PyPegen_run_parser()
result/exception check, and the tokenizer-init helpers.
Comment thread Parser/pegen.c
{
void *res = _PyPegen_parse(p);
assert(p->level == 0);
if (res != NULL && PyErr_Occurred()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a way to test this? We can force the allocation failure with _testcapi.set_nomemory (there is precedent in test_exceptions.py) and assert that compile() raises MemoryError instead of crashing on the contract assert.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind I think is fine

@pablogsal pablogsal added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes labels Jul 2, 2026
@pablogsal pablogsal merged commit 669299b into python:main Jul 2, 2026
64 checks passed
@miss-islington-app

Copy link
Copy Markdown

Thanks @tonghuaroot for the PR, and @pablogsal for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14, 3.15.
🐍🍒⛏🤖

@bedevere-app

bedevere-app Bot commented Jul 2, 2026

Copy link
Copy Markdown

GH-152836 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 pre-release feature fixes, bugs and security fixes label Jul 2, 2026
@miss-islington-app

Copy link
Copy Markdown

Sorry, @tonghuaroot and @pablogsal, I could not cleanly backport this to 3.13 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker 669299b62f6c2176d4bedcf550fb9baaabd6a9d6 3.13

@bedevere-app

bedevere-app Bot commented Jul 2, 2026

Copy link
Copy Markdown

GH-152837 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.14 bugs and security fixes label Jul 2, 2026
@bedevere-app

bedevere-app Bot commented Jul 2, 2026

Copy link
Copy Markdown

GH-152840 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.13 bugs and security fixes label Jul 2, 2026
@tonghuaroot

Copy link
Copy Markdown
Contributor Author

GH-152840 does the 3.13 backport. The conflict was only the _PyPegen_raise_tokenizer_init_error_PyTokenizer_raise_init_error rename plus the struct compiler context in new_compiler; the fix itself is identical. Built 3.13 and ran test_compile/test_exceptions/test_tokenize/test_syntax/test_builtin — all green.

pablogsal pushed a commit that referenced this pull request Jul 2, 2026
pablogsal pushed a commit that referenced this pull request Jul 2, 2026
pablogsal pushed a commit that referenced this pull request Jul 2, 2026
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.

2 participants