Skip to content

Commit a36adfa

Browse files
authored
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.
1 parent c691f20 commit a36adfa

File tree

1 file changed

+23
-22
lines changed

1 file changed

+23
-22
lines changed

Python/ceval_gil.h

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,7 @@ tstate_must_exit(PyThreadState *tstate)
196196
tstate->interp->runtime to support calls from Python daemon threads.
197197
After Py_Finalize() has been called, tstate can be a dangling pointer:
198198
point to PyThreadState freed memory. */
199-
_PyRuntimeState *runtime = &_PyRuntime;
200-
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
199+
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime);
201200
return (finalizing != NULL && finalizing != tstate);
202201
}
203202

@@ -243,20 +242,23 @@ take_gil(PyThreadState *tstate)
243242
}
244243

245244
while (_Py_atomic_load_relaxed(&gil->locked)) {
246-
int timed_out = 0;
247-
unsigned long saved_switchnum;
248-
249-
saved_switchnum = gil->switch_number;
250-
245+
unsigned long saved_switchnum = gil->switch_number;
251246

252247
unsigned long interval = (gil->interval >= 1 ? gil->interval : 1);
248+
int timed_out = 0;
253249
COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out);
250+
254251
/* If we timed out and no switch occurred in the meantime, it is time
255252
to ask the GIL-holding thread to drop it. */
256253
if (timed_out &&
257254
_Py_atomic_load_relaxed(&gil->locked) &&
258255
gil->switch_number == saved_switchnum)
259256
{
257+
if (tstate_must_exit(tstate)) {
258+
MUTEX_UNLOCK(gil->mutex);
259+
PyThread_exit_thread();
260+
}
261+
260262
SET_GIL_DROP_REQUEST(ceval);
261263
}
262264
}
@@ -281,6 +283,19 @@ take_gil(PyThreadState *tstate)
281283
MUTEX_UNLOCK(gil->switch_mutex);
282284
#endif
283285

286+
if (tstate_must_exit(tstate)) {
287+
/* bpo-36475: If Py_Finalize() has been called and tstate is not
288+
the thread which called Py_Finalize(), exit immediately the
289+
thread.
290+
291+
This code path can be reached by a daemon thread which was waiting
292+
in take_gil() while the main thread called
293+
wait_for_thread_shutdown() from Py_Finalize(). */
294+
MUTEX_UNLOCK(gil->mutex);
295+
drop_gil(ceval, ceval2, tstate);
296+
PyThread_exit_thread();
297+
}
298+
284299
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
285300
RESET_GIL_DROP_REQUEST(ceval, ceval2);
286301
}
@@ -293,27 +308,13 @@ take_gil(PyThreadState *tstate)
293308
COMPUTE_EVAL_BREAKER(ceval, ceval2);
294309
}
295310

296-
int must_exit = tstate_must_exit(tstate);
297-
298311
/* Don't access tstate if the thread must exit */
299-
if (!must_exit && tstate->async_exc != NULL) {
312+
if (tstate->async_exc != NULL) {
300313
_PyEval_SignalAsyncExc(tstate);
301314
}
302315

303316
MUTEX_UNLOCK(gil->mutex);
304317

305-
if (must_exit) {
306-
/* bpo-36475: If Py_Finalize() has been called and tstate is not
307-
the thread which called Py_Finalize(), exit immediately the
308-
thread.
309-
310-
This code path can be reached by a daemon thread which was waiting
311-
in take_gil() while the main thread called
312-
wait_for_thread_shutdown() from Py_Finalize(). */
313-
drop_gil(ceval, ceval2, tstate);
314-
PyThread_exit_thread();
315-
}
316-
317318
errno = err;
318319
}
319320

0 commit comments

Comments
 (0)