Skip to content

Commit a3c0318

Browse files
authored
gh-112723: Call PyThreadState_Clear() from the correct interpreter (#112776)
The `PyThreadState_Clear()` function must only be called with the GIL held and must be called from the same interpreter as the passed in thread state. Otherwise, any Python objects on the thread state may be destroyed using the wrong interpreter, leading to memory corruption. This is also important for `Py_GIL_DISABLED` builds because free lists will be associated with PyThreadStates and cleared in `PyThreadState_Clear()`. This fixes two places that called `PyThreadState_Clear()` from the wrong interpreter and adds an assertion to `PyThreadState_Clear()`.
1 parent 8a4c1f3 commit a3c0318

File tree

6 files changed

+34
-55
lines changed

6 files changed

+34
-55
lines changed

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ _PyEval_Vector(PyThreadState *tstate,
124124
PyObject *kwnames);
125125

126126
extern int _PyEval_ThreadsInitialized(void);
127-
extern PyStatus _PyEval_InitGIL(PyThreadState *tstate, int own_gil);
127+
extern void _PyEval_InitGIL(PyThreadState *tstate, int own_gil);
128128
extern void _PyEval_FiniGIL(PyInterpreterState *interp);
129129

130130
extern void _PyEval_AcquireLock(PyThreadState *tstate);

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ extern void _PyArg_Fini(void);
6363
extern void _Py_FinalizeAllocatedBlocks(_PyRuntimeState *);
6464

6565
extern PyStatus _PyGILState_Init(PyInterpreterState *interp);
66-
extern PyStatus _PyGILState_SetTstate(PyThreadState *tstate);
66+
extern void _PyGILState_SetTstate(PyThreadState *tstate);
6767
extern void _PyGILState_Fini(PyInterpreterState *interp);
6868

6969
extern void _PyGC_DumpShutdownStats(PyInterpreterState *interp);

Modules/_xxsubinterpretersmodule.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,9 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds)
547547
return NULL;
548548
}
549549

550+
PyThreadState_Swap(tstate);
550551
PyThreadState_Clear(tstate);
552+
PyThreadState_Swap(save_tstate);
551553
PyThreadState_Delete(tstate);
552554

553555
_PyInterpreterState_RequireIDRef(interp, 1);

Python/ceval_gil.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ init_own_gil(PyInterpreterState *interp, struct _gil_runtime_state *gil)
447447
interp->ceval.own_gil = 1;
448448
}
449449

450-
PyStatus
450+
void
451451
_PyEval_InitGIL(PyThreadState *tstate, int own_gil)
452452
{
453453
assert(tstate->interp->ceval.gil == NULL);
@@ -466,8 +466,6 @@ _PyEval_InitGIL(PyThreadState *tstate, int own_gil)
466466

467467
// Lock the GIL and mark the current thread as attached.
468468
_PyThreadState_Attach(tstate);
469-
470-
return _PyStatus_OK();
471469
}
472470

473471
void

Python/pylifecycle.c

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -576,44 +576,33 @@ init_interp_settings(PyInterpreterState *interp,
576576
interp->feature_flags |= Py_RTFLAGS_MULTI_INTERP_EXTENSIONS;
577577
}
578578

579-
/* We check "gil" in init_interp_create_gil(). */
579+
switch (config->gil) {
580+
case PyInterpreterConfig_DEFAULT_GIL: break;
581+
case PyInterpreterConfig_SHARED_GIL: break;
582+
case PyInterpreterConfig_OWN_GIL: break;
583+
default:
584+
return _PyStatus_ERR("invalid interpreter config 'gil' value");
585+
}
580586

581587
return _PyStatus_OK();
582588
}
583589

584590

585-
static PyStatus
591+
static void
586592
init_interp_create_gil(PyThreadState *tstate, int gil)
587593
{
588-
PyStatus status;
589-
590594
/* finalize_interp_delete() comment explains why _PyEval_FiniGIL() is
591595
only called here. */
592596
// XXX This is broken with a per-interpreter GIL.
593597
_PyEval_FiniGIL(tstate->interp);
594598

595599
/* Auto-thread-state API */
596-
status = _PyGILState_SetTstate(tstate);
597-
if (_PyStatus_EXCEPTION(status)) {
598-
return status;
599-
}
600+
_PyGILState_SetTstate(tstate);
600601

601-
int own_gil;
602-
switch (gil) {
603-
case PyInterpreterConfig_DEFAULT_GIL: own_gil = 0; break;
604-
case PyInterpreterConfig_SHARED_GIL: own_gil = 0; break;
605-
case PyInterpreterConfig_OWN_GIL: own_gil = 1; break;
606-
default:
607-
return _PyStatus_ERR("invalid interpreter config 'gil' value");
608-
}
602+
int own_gil = (gil == PyInterpreterConfig_OWN_GIL);
609603

610604
/* Create the GIL and take it */
611-
status = _PyEval_InitGIL(tstate, own_gil);
612-
if (_PyStatus_EXCEPTION(status)) {
613-
return status;
614-
}
615-
616-
return _PyStatus_OK();
605+
_PyEval_InitGIL(tstate, own_gil);
617606
}
618607

619608

@@ -657,10 +646,7 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
657646
}
658647
_PyThreadState_Bind(tstate);
659648

660-
status = init_interp_create_gil(tstate, config.gil);
661-
if (_PyStatus_EXCEPTION(status)) {
662-
return status;
663-
}
649+
init_interp_create_gil(tstate, config.gil);
664650

665651
*tstate_p = tstate;
666652
return _PyStatus_OK();
@@ -2099,28 +2085,21 @@ new_interpreter(PyThreadState **tstate_p, const PyInterpreterConfig *config)
20992085
return _PyStatus_OK();
21002086
}
21012087

2102-
PyThreadState *tstate = _PyThreadState_New(interp,
2103-
_PyThreadState_WHENCE_INTERP);
2104-
if (tstate == NULL) {
2105-
PyInterpreterState_Delete(interp);
2106-
*tstate_p = NULL;
2107-
return _PyStatus_OK();
2108-
}
2109-
_PyThreadState_Bind(tstate);
2110-
2088+
// XXX Might new_interpreter() have been called without the GIL held?
21112089
PyThreadState *save_tstate = _PyThreadState_GET();
2112-
int has_gil = 0;
2090+
PyThreadState *tstate = NULL;
21132091

21142092
/* From this point until the init_interp_create_gil() call,
21152093
we must not do anything that requires that the GIL be held
21162094
(or otherwise exist). That applies whether or not the new
21172095
interpreter has its own GIL (e.g. the main interpreter). */
2096+
if (save_tstate != NULL) {
2097+
_PyThreadState_Detach(save_tstate);
2098+
}
21182099

21192100
/* Copy the current interpreter config into the new interpreter */
21202101
const PyConfig *src_config;
21212102
if (save_tstate != NULL) {
2122-
// XXX Might new_interpreter() have been called without the GIL held?
2123-
_PyThreadState_Detach(save_tstate);
21242103
src_config = _PyInterpreterState_GetConfig(save_tstate->interp);
21252104
}
21262105
else
@@ -2142,11 +2121,14 @@ new_interpreter(PyThreadState **tstate_p, const PyInterpreterConfig *config)
21422121
goto error;
21432122
}
21442123

2145-
status = init_interp_create_gil(tstate, config->gil);
2146-
if (_PyStatus_EXCEPTION(status)) {
2124+
tstate = _PyThreadState_New(interp, _PyThreadState_WHENCE_INTERP);
2125+
if (tstate == NULL) {
2126+
status = _PyStatus_NO_MEMORY();
21472127
goto error;
21482128
}
2149-
has_gil = 1;
2129+
2130+
_PyThreadState_Bind(tstate);
2131+
init_interp_create_gil(tstate, config->gil);
21502132

21512133
/* No objects have been created yet. */
21522134

@@ -2165,16 +2147,14 @@ new_interpreter(PyThreadState **tstate_p, const PyInterpreterConfig *config)
21652147

21662148
error:
21672149
*tstate_p = NULL;
2168-
2169-
/* Oops, it didn't work. Undo it all. */
2170-
if (has_gil) {
2150+
if (tstate != NULL) {
2151+
PyThreadState_Clear(tstate);
21712152
_PyThreadState_Detach(tstate);
2153+
PyThreadState_Delete(tstate);
21722154
}
21732155
if (save_tstate != NULL) {
21742156
_PyThreadState_Attach(save_tstate);
21752157
}
2176-
PyThreadState_Clear(tstate);
2177-
PyThreadState_Delete(tstate);
21782158
PyInterpreterState_Delete(interp);
21792159

21802160
return status;

Python/pystate.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,7 @@ void
14541454
PyThreadState_Clear(PyThreadState *tstate)
14551455
{
14561456
assert(tstate->_status.initialized && !tstate->_status.cleared);
1457+
assert(current_fast_get(&_PyRuntime)->interp == tstate->interp);
14571458
// XXX assert(!tstate->_status.bound || tstate->_status.unbound);
14581459
tstate->_status.finalizing = 1; // just in case
14591460

@@ -2150,7 +2151,7 @@ _PyGILState_Fini(PyInterpreterState *interp)
21502151

21512152

21522153
// XXX Drop this.
2153-
PyStatus
2154+
void
21542155
_PyGILState_SetTstate(PyThreadState *tstate)
21552156
{
21562157
/* must init with valid states */
@@ -2160,7 +2161,7 @@ _PyGILState_SetTstate(PyThreadState *tstate)
21602161
if (!_Py_IsMainInterpreter(tstate->interp)) {
21612162
/* Currently, PyGILState is shared by all interpreters. The main
21622163
* interpreter is responsible to initialize it. */
2163-
return _PyStatus_OK();
2164+
return;
21642165
}
21652166

21662167
#ifndef NDEBUG
@@ -2170,8 +2171,6 @@ _PyGILState_SetTstate(PyThreadState *tstate)
21702171
assert(gilstate_tss_get(runtime) == tstate);
21712172
assert(tstate->gilstate_counter == 1);
21722173
#endif
2173-
2174-
return _PyStatus_OK();
21752174
}
21762175

21772176
PyInterpreterState *

0 commit comments

Comments
 (0)