https://github.com/python/cpython/commit/ef844c8f71d72f33b1469a09b747d80b9295e75c commit: ef844c8f71d72f33b1469a09b747d80b9295e75c branch: 3.14 author: Miss Islington (bot) <[email protected]> committer: JelleZijlstra <[email protected]> date: 2026-10-23T00:42:33Z summary:
[3.14] gh-159739: Release replaced type alias values (GH-160167) (#160243) A type alias can be evaluated again before an earlier evaluation finishes, either recursively or in another thread. The later completion overwrites the cached value without releasing it, permanently leaking that value; synchronize cache access and release the replaced value outside the lock. Backport to 3.15 and 3.14. (cherry picked from commit d36a3f6970407bb91dd863a10f0b42425a7e954e) Co-authored-by: Jelle Zijlstra <[email protected]> files: A Misc/NEWS.d/next/Core_and_Builtins/2026-10-21-19-55-00.gh-issue-159739.tali01.rst M Lib/test/test_type_aliases.py M Objects/typevarobject.c diff --git a/Lib/test/test_type_aliases.py b/Lib/test/test_type_aliases.py index ee1791bc1d0b9d..eeb7910516962d 100644 --- a/Lib/test/test_type_aliases.py +++ b/Lib/test/test_type_aliases.py @@ -1,7 +1,8 @@ import pickle import types import unittest -from test.support import check_syntax_error, run_code +import weakref +from test.support import check_syntax_error, gc_collect, run_code from test.typinganndata import mod_generics_cache from typing import ( @@ -124,6 +125,24 @@ def more_generic[T, *Ts, **P](): T, Ts, P = more_generic.__type_params__ self.assertEqual(alias.__value__, tuple[Callable[P, tuple[T, *Ts]], Callable[P2, tuple[T2, *Ts2]]]) + def test_reentrant_value_does_not_leak(self): + class Value: + pass + + refs = [] + + def compute(): + value = Value() + refs.append(weakref.ref(value)) + if len(refs) == 1: + Alias.__value__ + return value + + type Alias = compute() + self.assertIs(Alias.__value__, refs[0]()) + gc_collect() + self.assertIsNone(refs[1]()) + def test_subscripting(self): type NonGeneric = int type Generic[A] = dict[A, A] diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-21-19-55-00.gh-issue-159739.tali01.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-21-19-55-00.gh-issue-159739.tali01.rst new file mode 100644 index 00000000000000..a4f672fed87cfe --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-21-19-55-00.gh-issue-159739.tali01.rst @@ -0,0 +1,2 @@ +Fix a reference leak when the value of a type alias is evaluated +recursively or concurrently. diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 628963b5770214..97e913b6c268c0 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -1858,14 +1858,23 @@ typealias_dealloc(PyObject *self) static PyObject * typealias_get_value(typealiasobject *ta) { - if (ta->value != NULL) { - return Py_NewRef(ta->value); - } - PyObject *result = PyObject_CallNoArgs(ta->compute_value); + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(ta); + result = Py_XNewRef(ta->value); + Py_END_CRITICAL_SECTION(); + if (result != NULL) { + return result; + } + result = PyObject_CallNoArgs(ta->compute_value); if (result == NULL) { return NULL; } + PyObject *old_value; + Py_BEGIN_CRITICAL_SECTION(ta); + old_value = ta->value; ta->value = Py_NewRef(result); + Py_END_CRITICAL_SECTION(); + Py_XDECREF(old_value); return result; } _______________________________________________ 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]
