Skip to content

gh-152682: Fix NULL dereference on OOM in symtable_visit_type_param_bound_or_default#152684

Merged
sobolevn merged 1 commit into
python:mainfrom
petrvaganoff:dev
Jun 30, 2026
Merged

gh-152682: Fix NULL dereference on OOM in symtable_visit_type_param_bound_or_default#152684
sobolevn merged 1 commit into
python:mainfrom
petrvaganoff:dev

Conversation

@petrvaganoff

@petrvaganoff petrvaganoff commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

When a reserved name (e.g. __classdict__) is used as a type parameter,
symtable_visit_type_param_bound_or_default() calls PyUnicode_FromFormat()
to build the SyntaxError message. If the allocation fails and returns NULL,
the subsequent PyErr_SetObject(PyExc_SyntaxError, NULL) and Py_DECREF(NULL)
calls dereference the null pointer, causing a segfault.

Fix by returning 0 immediately when PyUnicode_FromFormat() returns NULL,
which propagates the MemoryError already set by PyUnicode_FromFormat().

The bug was introduced in gh-128632 (commit 891c61c).

Found by static analyzer Svace (ISP RAS).

  • Add a NEWS entry in Misc/NEWS.d/
  • Add regression test (test_disallowed_type_param_names_oom)

@python-cla-bot

python-cla-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

@sobolevn sobolevn left a comment

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.

Don't forget to add else: raise RuntimeError('Not raised') as well :)

Comment thread Lib/test/test_syntax.py Outdated
code = textwrap.dedent("""\
import _testcapi
_testcapi.set_nomemory(0)
compile("class A[__classdict__]: pass", "<string>", "exec")

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.

Can we catch the MemoryError here? And just assert that script works correctly with assert_python_ok?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, done, updated the test to catch MemoryError and use assert_python_ok. Imports fixed.

…aram_bound_or_default

In symtable_visit_type_param_bound_or_default(), when a reserved name
(e.g. __classdict__) is used as a type parameter, PyUnicode_FromFormat()
is called to build the SyntaxError message. If the allocation fails and
returns NULL, the subsequent PyErr_SetObject() and Py_DECREF() calls
would dereference NULL, causing a segfault.

Fix by returning 0 immediately when PyUnicode_FromFormat() returns NULL.
This propagates the MemoryError set by PyUnicode_FromFormat().

The bug was introduced in pythongh-128632 (commit 891c61c).
@sobolevn sobolevn 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 Jun 30, 2026
@sobolevn sobolevn enabled auto-merge (squash) June 30, 2026 14:15
@sobolevn sobolevn merged commit 10ed03e into python:main Jun 30, 2026
60 checks passed
@miss-islington-app

Copy link
Copy Markdown

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

@bedevere-app

bedevere-app Bot commented Jun 30, 2026

Copy link
Copy Markdown

GH-152695 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 Jun 30, 2026
@bedevere-app

bedevere-app Bot commented Jun 30, 2026

Copy link
Copy Markdown

GH-152696 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 Jun 30, 2026
@bedevere-app

bedevere-app Bot commented Jun 30, 2026

Copy link
Copy Markdown

GH-152697 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 Jun 30, 2026
@sobolevn

Copy link
Copy Markdown
Member

Congrats on your first CPython contribution! 🎉

Comment thread Lib/test/test_syntax.py
""", "<testcase>", mode="exec")

@support.nomemtest
def test_disallowed_type_param_names_oom(self):

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.

This test passes for me on main.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I also couldn't reproduce it reliably. After investigating, it seems _testcapi.set_nomemory(0) replaces the global CPython allocator with a stub, and there are many allocations happening between the set_nomemory(0) call and PyUnicode_FromFormat inside compile(), so OOM typically hits one of them first. I verified this with a coverage build - the relevant lines are never reached.

I also tried manually injecting an OOM condition in the C code - without the fix it does produce SIGSEGV, confirming the bug is real.

In principle, we could add a fault injection hook in the debug build and expose it via _testinternalcapi. But I think the simpler option is to drop the OOM test entirely - the fix is a straightforward NULL check, and the existing test_disallowed_type_param_names() already covers the code path. What do you think?

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.

3 participants