bpo-39877: 4th take_gil() fix for daemon threads (GH-19080)
bpo-39877, bpo-40010: Add a third tstate_must_exit() check in take_gil() to prevent using tstate which has been freed.
This commit is contained in:
parent
c691f20952
commit
a36adfa6bb
@ -196,8 +196,7 @@ tstate_must_exit(PyThreadState *tstate)
|
|||||||
tstate->interp->runtime to support calls from Python daemon threads.
|
tstate->interp->runtime to support calls from Python daemon threads.
|
||||||
After Py_Finalize() has been called, tstate can be a dangling pointer:
|
After Py_Finalize() has been called, tstate can be a dangling pointer:
|
||||||
point to PyThreadState freed memory. */
|
point to PyThreadState freed memory. */
|
||||||
_PyRuntimeState *runtime = &_PyRuntime;
|
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime);
|
||||||
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
|
|
||||||
return (finalizing != NULL && finalizing != tstate);
|
return (finalizing != NULL && finalizing != tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,20 +242,23 @@ take_gil(PyThreadState *tstate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (_Py_atomic_load_relaxed(&gil->locked)) {
|
while (_Py_atomic_load_relaxed(&gil->locked)) {
|
||||||
int timed_out = 0;
|
unsigned long saved_switchnum = gil->switch_number;
|
||||||
unsigned long saved_switchnum;
|
|
||||||
|
|
||||||
saved_switchnum = gil->switch_number;
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long interval = (gil->interval >= 1 ? gil->interval : 1);
|
unsigned long interval = (gil->interval >= 1 ? gil->interval : 1);
|
||||||
|
int timed_out = 0;
|
||||||
COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out);
|
COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out);
|
||||||
|
|
||||||
/* If we timed out and no switch occurred in the meantime, it is time
|
/* If we timed out and no switch occurred in the meantime, it is time
|
||||||
to ask the GIL-holding thread to drop it. */
|
to ask the GIL-holding thread to drop it. */
|
||||||
if (timed_out &&
|
if (timed_out &&
|
||||||
_Py_atomic_load_relaxed(&gil->locked) &&
|
_Py_atomic_load_relaxed(&gil->locked) &&
|
||||||
gil->switch_number == saved_switchnum)
|
gil->switch_number == saved_switchnum)
|
||||||
{
|
{
|
||||||
|
if (tstate_must_exit(tstate)) {
|
||||||
|
MUTEX_UNLOCK(gil->mutex);
|
||||||
|
PyThread_exit_thread();
|
||||||
|
}
|
||||||
|
|
||||||
SET_GIL_DROP_REQUEST(ceval);
|
SET_GIL_DROP_REQUEST(ceval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,6 +283,19 @@ _ready:
|
|||||||
MUTEX_UNLOCK(gil->switch_mutex);
|
MUTEX_UNLOCK(gil->switch_mutex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (tstate_must_exit(tstate)) {
|
||||||
|
/* bpo-36475: If Py_Finalize() has been called and tstate is not
|
||||||
|
the thread which called Py_Finalize(), exit immediately the
|
||||||
|
thread.
|
||||||
|
|
||||||
|
This code path can be reached by a daemon thread which was waiting
|
||||||
|
in take_gil() while the main thread called
|
||||||
|
wait_for_thread_shutdown() from Py_Finalize(). */
|
||||||
|
MUTEX_UNLOCK(gil->mutex);
|
||||||
|
drop_gil(ceval, ceval2, tstate);
|
||||||
|
PyThread_exit_thread();
|
||||||
|
}
|
||||||
|
|
||||||
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
||||||
RESET_GIL_DROP_REQUEST(ceval, ceval2);
|
RESET_GIL_DROP_REQUEST(ceval, ceval2);
|
||||||
}
|
}
|
||||||
@ -293,27 +308,13 @@ _ready:
|
|||||||
COMPUTE_EVAL_BREAKER(ceval, ceval2);
|
COMPUTE_EVAL_BREAKER(ceval, ceval2);
|
||||||
}
|
}
|
||||||
|
|
||||||
int must_exit = tstate_must_exit(tstate);
|
|
||||||
|
|
||||||
/* Don't access tstate if the thread must exit */
|
/* Don't access tstate if the thread must exit */
|
||||||
if (!must_exit && tstate->async_exc != NULL) {
|
if (tstate->async_exc != NULL) {
|
||||||
_PyEval_SignalAsyncExc(tstate);
|
_PyEval_SignalAsyncExc(tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
MUTEX_UNLOCK(gil->mutex);
|
MUTEX_UNLOCK(gil->mutex);
|
||||||
|
|
||||||
if (must_exit) {
|
|
||||||
/* bpo-36475: If Py_Finalize() has been called and tstate is not
|
|
||||||
the thread which called Py_Finalize(), exit immediately the
|
|
||||||
thread.
|
|
||||||
|
|
||||||
This code path can be reached by a daemon thread which was waiting
|
|
||||||
in take_gil() while the main thread called
|
|
||||||
wait_for_thread_shutdown() from Py_Finalize(). */
|
|
||||||
drop_gil(ceval, ceval2, tstate);
|
|
||||||
PyThread_exit_thread();
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = err;
|
errno = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user