https://github.com/python/cpython/commit/28dcc8e87d526cfeafd74f8373b19da427875d66
commit: 28dcc8e87d526cfeafd74f8373b19da427875d66
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-10-22T15:19:59-07:00
summary:

[3.15] gh-159739: Allocate thread-local state before constructing the object 
(GH-160166) (#160286)



If allocating a thread-local object’s internal dictionary or watchdog set 
fails, a subclass finalizer can run with that state missing. Accessing a 
thread-local attribute then dereferences the missing state and crashes; 
allocate both containers before creating the object. Backport to 3.15 and 3.14.
(cherry picked from commit 4a61673276361aa4f8e86a7261d818a83f99b11e)

Co-authored-by: Jelle Zijlstra <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-10-21-19-55-00.gh-issue-159739.tloc01.rst
M Modules/_threadmodule.c

diff --git 
a/Misc/NEWS.d/next/Library/2026-10-21-19-55-00.gh-issue-159739.tloc01.rst 
b/Misc/NEWS.d/next/Library/2026-10-21-19-55-00.gh-issue-159739.tloc01.rst
new file mode 100644
index 00000000000000..9641d11deb84d6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-21-19-55-00.gh-issue-159739.tloc01.rst
@@ -0,0 +1,2 @@
+Avoid a crash when a :class:`threading.local` subclass finalizer accesses
+attributes after construction fails.
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index 199e4ac3db723b..81ff9af1bd97c6 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -1521,10 +1521,25 @@ local_new(PyTypeObject *type, PyObject *args, PyObject 
*kw)
     assert(module != NULL);
     thread_module_state *state = get_thread_state(module);
 
+    // A subclass finalizer may access attributes if construction fails.
+    PyObject *localdicts = PyDict_New();
+    if (localdicts == NULL) {
+        return NULL;
+    }
+    PyObject *thread_watchdogs = PySet_New(NULL);
+    if (thread_watchdogs == NULL) {
+        Py_DECREF(localdicts);
+        return NULL;
+    }
+
     localobject *self = (localobject *)type->tp_alloc(type, 0);
     if (self == NULL) {
+        Py_DECREF(localdicts);
+        Py_DECREF(thread_watchdogs);
         return NULL;
     }
+    self->localdicts = localdicts;
+    self->thread_watchdogs = thread_watchdogs;
 
     // gh-128691: Use deferred reference counting for thread-locals to avoid
     // contention on the shared object.
@@ -1533,16 +1548,6 @@ local_new(PyTypeObject *type, PyObject *args, PyObject 
*kw)
     self->args = Py_XNewRef(args);
     self->kw = Py_XNewRef(kw);
 
-    self->localdicts = PyDict_New();
-    if (self->localdicts == NULL) {
-        goto err;
-    }
-
-    self->thread_watchdogs = PySet_New(NULL);
-    if (self->thread_watchdogs == NULL) {
-        goto err;
-    }
-
     PyObject *localsdict = NULL;
     PyObject *sentinel_wr = NULL;
     if (create_localsdict(self, state, &localsdict, &sentinel_wr) < 0) {

_______________________________________________
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]

Reply via email to