mirror of https://github.com/python/cpython
gh-104690: thread_run() checks for tstate dangling pointer (#109056)
thread_run() of _threadmodule.c now calls _PyThreadState_CheckConsistency() to check if tstate is a dangling pointer when Python is built in debug mode. Rename ceval_gil.c is_tstate_valid() to _PyThreadState_CheckConsistency() to reuse it in _threadmodule.c.
This commit is contained in:
parent
b0edf3b98e
commit
f63d37877a
|
@ -67,6 +67,10 @@ _Py_ThreadCanHandleSignals(PyInterpreterState *interp)
|
||||||
extern _Py_thread_local PyThreadState *_Py_tss_tstate;
|
extern _Py_thread_local PyThreadState *_Py_tss_tstate;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
extern int _PyThreadState_CheckConsistency(PyThreadState *tstate);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Export for most shared extensions, used via _PyThreadState_GET() static
|
// Export for most shared extensions, used via _PyThreadState_GET() static
|
||||||
// inline function.
|
// inline function.
|
||||||
PyAPI_FUNC(PyThreadState *) _PyThreadState_GetCurrent(void);
|
PyAPI_FUNC(PyThreadState *) _PyThreadState_GetCurrent(void);
|
||||||
|
|
|
@ -1074,9 +1074,12 @@ static void
|
||||||
thread_run(void *boot_raw)
|
thread_run(void *boot_raw)
|
||||||
{
|
{
|
||||||
struct bootstate *boot = (struct bootstate *) boot_raw;
|
struct bootstate *boot = (struct bootstate *) boot_raw;
|
||||||
PyThreadState *tstate;
|
PyThreadState *tstate = boot->tstate;
|
||||||
|
|
||||||
|
// gh-104690: If Python is being finalized and PyInterpreterState_Delete()
|
||||||
|
// was called, tstate becomes a dangling pointer.
|
||||||
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
|
|
||||||
tstate = boot->tstate;
|
|
||||||
_PyThreadState_Bind(tstate);
|
_PyThreadState_Bind(tstate);
|
||||||
PyEval_AcquireThread(tstate);
|
PyEval_AcquireThread(tstate);
|
||||||
tstate->interp->threads.count++;
|
tstate->interp->threads.count++;
|
||||||
|
|
|
@ -163,16 +163,6 @@ UNSIGNAL_ASYNC_EXC(PyInterpreterState *interp)
|
||||||
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
/* Ensure that tstate is valid */
|
|
||||||
static int
|
|
||||||
is_tstate_valid(PyThreadState *tstate)
|
|
||||||
{
|
|
||||||
assert(!_PyMem_IsPtrFreed(tstate));
|
|
||||||
assert(!_PyMem_IsPtrFreed(tstate->interp));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of the Global Interpreter Lock (GIL).
|
* Implementation of the Global Interpreter Lock (GIL).
|
||||||
|
@ -325,7 +315,7 @@ drop_gil(struct _ceval_state *ceval, PyThreadState *tstate)
|
||||||
/* Not switched yet => wait */
|
/* Not switched yet => wait */
|
||||||
if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
|
if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
|
||||||
{
|
{
|
||||||
assert(is_tstate_valid(tstate));
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
RESET_GIL_DROP_REQUEST(tstate->interp);
|
RESET_GIL_DROP_REQUEST(tstate->interp);
|
||||||
/* NOTE: if COND_WAIT does not atomically start waiting when
|
/* NOTE: if COND_WAIT does not atomically start waiting when
|
||||||
releasing the mutex, another thread can run through, take
|
releasing the mutex, another thread can run through, take
|
||||||
|
@ -386,7 +376,7 @@ take_gil(PyThreadState *tstate)
|
||||||
PyThread_exit_thread();
|
PyThread_exit_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(is_tstate_valid(tstate));
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
PyInterpreterState *interp = tstate->interp;
|
PyInterpreterState *interp = tstate->interp;
|
||||||
struct _ceval_state *ceval = &interp->ceval;
|
struct _ceval_state *ceval = &interp->ceval;
|
||||||
struct _gil_runtime_state *gil = ceval->gil;
|
struct _gil_runtime_state *gil = ceval->gil;
|
||||||
|
@ -427,7 +417,7 @@ take_gil(PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
PyThread_exit_thread();
|
PyThread_exit_thread();
|
||||||
}
|
}
|
||||||
assert(is_tstate_valid(tstate));
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
|
|
||||||
SET_GIL_DROP_REQUEST(interp);
|
SET_GIL_DROP_REQUEST(interp);
|
||||||
drop_requested = 1;
|
drop_requested = 1;
|
||||||
|
@ -466,7 +456,7 @@ _ready:
|
||||||
drop_gil(ceval, tstate);
|
drop_gil(ceval, tstate);
|
||||||
PyThread_exit_thread();
|
PyThread_exit_thread();
|
||||||
}
|
}
|
||||||
assert(is_tstate_valid(tstate));
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
|
|
||||||
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
||||||
RESET_GIL_DROP_REQUEST(interp);
|
RESET_GIL_DROP_REQUEST(interp);
|
||||||
|
@ -679,7 +669,7 @@ PyEval_AcquireThread(PyThreadState *tstate)
|
||||||
void
|
void
|
||||||
PyEval_ReleaseThread(PyThreadState *tstate)
|
PyEval_ReleaseThread(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
assert(is_tstate_valid(tstate));
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
|
|
||||||
PyThreadState *new_tstate = _PyThreadState_SwapNoGIL(NULL);
|
PyThreadState *new_tstate = _PyThreadState_SwapNoGIL(NULL);
|
||||||
if (new_tstate != tstate) {
|
if (new_tstate != tstate) {
|
||||||
|
@ -877,7 +867,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
|
||||||
static int
|
static int
|
||||||
handle_signals(PyThreadState *tstate)
|
handle_signals(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
assert(is_tstate_valid(tstate));
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
if (!_Py_ThreadCanHandleSignals(tstate->interp)) {
|
if (!_Py_ThreadCanHandleSignals(tstate->interp)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -983,7 +973,7 @@ void
|
||||||
_Py_FinishPendingCalls(PyThreadState *tstate)
|
_Py_FinishPendingCalls(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
assert(PyGILState_Check());
|
assert(PyGILState_Check());
|
||||||
assert(is_tstate_valid(tstate));
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
|
|
||||||
if (make_pending_calls(tstate->interp) < 0) {
|
if (make_pending_calls(tstate->interp) < 0) {
|
||||||
PyObject *exc = _PyErr_GetRaisedException(tstate);
|
PyObject *exc = _PyErr_GetRaisedException(tstate);
|
||||||
|
@ -1024,7 +1014,7 @@ Py_MakePendingCalls(void)
|
||||||
assert(PyGILState_Check());
|
assert(PyGILState_Check());
|
||||||
|
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
assert(is_tstate_valid(tstate));
|
assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
|
|
||||||
/* Only execute pending calls on the main thread. */
|
/* Only execute pending calls on the main thread. */
|
||||||
if (!_Py_IsMainThread() || !_Py_IsMainInterpreter(tstate->interp)) {
|
if (!_Py_IsMainThread() || !_Py_IsMainInterpreter(tstate->interp)) {
|
||||||
|
|
|
@ -2890,6 +2890,24 @@ _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame * frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// Check that a Python thread state valid. In practice, this function is used
|
||||||
|
// on a Python debug build to check if 'tstate' is a dangling pointer, if the
|
||||||
|
// PyThreadState memory has been freed.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// assert(_PyThreadState_CheckConsistency(tstate));
|
||||||
|
int
|
||||||
|
_PyThreadState_CheckConsistency(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
assert(!_PyMem_IsPtrFreed(tstate));
|
||||||
|
assert(!_PyMem_IsPtrFreed(tstate->interp));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue