https://github.com/python/cpython/commit/4a61673276361aa4f8e86a7261d818a83f99b11e
commit: 4a61673276361aa4f8e86a7261d818a83f99b11e
branch: main
author: Jelle Zijlstra <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-10-22T11:22:09-07:00
summary:
gh-159739: Allocate thread-local state before constructing the object (#160166)
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.
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 058d16f14f290e..2b227a4eec3721 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -1542,10 +1542,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.
@@ -1554,16 +1569,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]