Skip to content

Commit 4a61673

Browse files
authored
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.
1 parent 5ce1ef3 commit 4a61673

2 files changed

Lines changed: 17 additions & 10 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid a crash when a :class:`threading.local` subclass finalizer accesses
2+
attributes after construction fails.

‎Modules/_threadmodule.c‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,10 +1542,25 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
15421542
assert(module != NULL);
15431543
thread_module_state *state = get_thread_state(module);
15441544

1545+
// A subclass finalizer may access attributes if construction fails.
1546+
PyObject *localdicts = PyDict_New();
1547+
if (localdicts == NULL) {
1548+
return NULL;
1549+
}
1550+
PyObject *thread_watchdogs = PySet_New(NULL);
1551+
if (thread_watchdogs == NULL) {
1552+
Py_DECREF(localdicts);
1553+
return NULL;
1554+
}
1555+
15451556
localobject *self = (localobject *)type->tp_alloc(type, 0);
15461557
if (self == NULL) {
1558+
Py_DECREF(localdicts);
1559+
Py_DECREF(thread_watchdogs);
15471560
return NULL;
15481561
}
1562+
self->localdicts = localdicts;
1563+
self->thread_watchdogs = thread_watchdogs;
15491564

15501565
// gh-128691: Use deferred reference counting for thread-locals to avoid
15511566
// contention on the shared object.
@@ -1554,16 +1569,6 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
15541569
self->args = Py_XNewRef(args);
15551570
self->kw = Py_XNewRef(kw);
15561571

1557-
self->localdicts = PyDict_New();
1558-
if (self->localdicts == NULL) {
1559-
goto err;
1560-
}
1561-
1562-
self->thread_watchdogs = PySet_New(NULL);
1563-
if (self->thread_watchdogs == NULL) {
1564-
goto err;
1565-
}
1566-
15671572
PyObject *localsdict = NULL;
15681573
PyObject *sentinel_wr = NULL;
15691574
if (create_localsdict(self, state, &localsdict, &sentinel_wr) < 0) {

0 commit comments

Comments
 (0)