[3.15] gh-154902: Type-check the SET_ADD operand (#155071)
hugovk <[email protected]> Tue, 04 Aug 2026 06:08:24 -0400 (EDT)
| Newsgroups | gmane.comp.python.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://github.com/python/cpython/commit/398dbef62a2542644b2c1e31d25bcc61837671d5 commit: 398dbef62a2542644b2c1e31d25bcc61837671d5 branch: 3.15 author: Timofei Ivankov <[email protected]> committer: hugovk <[email protected]> date: 2026-08-04T13:03:28+03:00 summary: [3.15] gh-154902: Type-check the SET_ADD operand (#155071) files: A Misc/NEWS.d/next/Core_and_Builtins/2026-08-02-11-47-15.gh-issue-154902.DIi6sf.rst M Lib/test/test_type_annotations.py M Modules/_testinternalcapi/test_cases.c.h M Python/bytecodes.c M Python/executor_cases.c.h M Python/generated_cases.c.h diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py index b751f825bb97d59..54db123a5dc2bf3 100644 --- a/Lib/test/test_type_annotations.py +++ b/Lib/test/test_type_annotations.py @@ -1,5 +1,6 @@ import annotationlib import inspect +import itertools import textwrap import types import unittest @@ -896,3 +897,18 @@ class Generic: mod = build_module(code) annos = mod.__annotations__ self.assertEqual(annos, {"annotated_name": 0}) + + # gh-154902 + def test_conditional_annotations_rebound(self): + # user code can rebind __conditional_annotations__ to any object + lefts = ("__conditional_annotations__", + 'globals()["__conditional_annotations__"]') + values = ("0", "{}", "[]", "''", "object()", "frozenset()") + for left, value in itertools.product(lefts, values): + with self.subTest(left=left, value=value): + code = f""" + {left} = {value} + x: int + """ + with self.assertRaises(TypeError): + run_code(code) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-02-11-47-15.gh-issue-154902.DIi6sf.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-02-11-47-15.gh-issue-154902.DIi6sf.rst new file mode 100644 index 000000000000000..49f34eaeddec4e4 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-02-11-47-15.gh-issue-154902.DIi6sf.rst @@ -0,0 +1,2 @@ +Fix a crash when ``__conditional_annotations__`` is rebound to a non-set +object. diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index e19ff840458e0e0..c51ed3c812523a7 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -11424,8 +11424,21 @@ _PyStackRef v; v = stack_pointer[-1]; set = stack_pointer[-2 - (oparg-1)]; + PyObject *set_o = PyStackRef_AsPyObjectBorrow(set); + if (!PySet_CheckExact(set_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'%T' object is not a set", set_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PySet_AddTakeRef((PySetObject *)PyStackRef_AsPyObjectBorrow(set), + int err = _PySet_AddTakeRef((PySetObject *)set_o, PyStackRef_AsPyObjectSteal(v)); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f39a5f0b07a5fcc..4f0d0227cc2eb04 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1378,7 +1378,15 @@ dummy_func( } inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) { - int err = _PySet_AddTakeRef((PySetObject *)PyStackRef_AsPyObjectBorrow(set), + PyObject *set_o = PyStackRef_AsPyObjectBorrow(set); + // gh-154902: user code can rebind __conditional_annotations__ + if (!PySet_CheckExact(set_o)) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%T' object is not a set", set_o); + PyStackRef_CLOSE(v); + ERROR_IF(true); + } + int err = _PySet_AddTakeRef((PySetObject *)set_o, PyStackRef_AsPyObjectSteal(v)); ERROR_IF(err); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4d5a8ab6b8af0b9..7644c2d38d7f068 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -8212,11 +8212,28 @@ oparg = CURRENT_OPARG(); v = _stack_item_0; set = stack_pointer[-1 - (oparg-1)]; + PyObject *set_o = PyStackRef_AsPyObjectBorrow(set); + if (!PySet_CheckExact(set_o)) { + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'%T' object is not a set", set_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } stack_pointer[0] = v; stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PySet_AddTakeRef((PySetObject *)PyStackRef_AsPyObjectBorrow(set), + int err = _PySet_AddTakeRef((PySetObject *)set_o, PyStackRef_AsPyObjectSteal(v)); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 512c358014fcffe..741306f7cf27f7c 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -11421,8 +11421,21 @@ _PyStackRef v; v = stack_pointer[-1]; set = stack_pointer[-2 - (oparg-1)]; + PyObject *set_o = PyStackRef_AsPyObjectBorrow(set); + if (!PySet_CheckExact(set_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'%T' object is not a set", set_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PySet_AddTakeRef((PySetObject *)PyStackRef_AsPyObjectBorrow(set), + int err = _PySet_AddTakeRef((PySetObject *)set_o, PyStackRef_AsPyObjectSteal(v)); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]