summaryrefslogtreecommitdiffstats
path: root/Python/ceval_gil.c
diff options
context:
space:
mode:
authorJeremy Maitin-Shepard <jeremy@jeremyms.com>2024-10-02 16:17:49 (GMT)
committerGitHub <noreply@github.com>2024-10-02 16:17:49 (GMT)
commit8cc5aa47ee464ddfd8da5461edecf4a5c72df2ff (patch)
tree41cdda0e2be153a4363cec9d616856444b306273 /Python/ceval_gil.c
parent113b2d7583cdbf79da18e696f299a9aca24b599b (diff)
downloadcpython-8cc5aa47ee464ddfd8da5461edecf4a5c72df2ff.zip
cpython-8cc5aa47ee464ddfd8da5461edecf4a5c72df2ff.tar.gz
cpython-8cc5aa47ee464ddfd8da5461edecf4a5c72df2ff.tar.bz2
gh-87135: Hang non-main threads that attempt to acquire the GIL during finalization (GH-105805)
Instead of surprise crashes and memory corruption, we now hang threads that attempt to re-enter the Python interpreter after Python runtime finalization has started. These are typically daemon threads (our long standing mis-feature) but could also be threads spawned by extension modules that then try to call into Python. This marks the `PyThread_exit_thread` public C API as deprecated as there is no plausible safe way to accomplish that on any supported platform in the face of things like C++ code with finalizers anywhere on a thread's stack. Doing this was the least bad option. Co-authored-by: Gregory P. Smith <greg@krypto.org>
Diffstat (limited to 'Python/ceval_gil.c')
-rw-r--r--Python/ceval_gil.c26
1 files changed, 17 insertions, 9 deletions
diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c
index 1d9381d..4c9f59f 100644
--- a/Python/ceval_gil.c
+++ b/Python/ceval_gil.c
@@ -7,6 +7,7 @@
#include "pycore_pylifecycle.h" // _PyErr_Print()
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystats.h" // _Py_PrintSpecializationStats()
+#include "pycore_pythread.h" // PyThread_hang_thread()
/*
Notes about the implementation:
@@ -277,10 +278,9 @@ drop_gil(PyInterpreterState *interp, PyThreadState *tstate, int final_release)
/* Take the GIL.
The function saves errno at entry and restores its value at exit.
+ It may hang rather than return if the interpreter has been finalized.
- tstate must be non-NULL.
-
- Returns 1 if the GIL was acquired, or 0 if not. */
+ tstate must be non-NULL. */
static void
take_gil(PyThreadState *tstate)
{
@@ -293,12 +293,18 @@ take_gil(PyThreadState *tstate)
if (_PyThreadState_MustExit(tstate)) {
/* bpo-39877: If Py_Finalize() has been called and tstate is not the
- thread which called Py_Finalize(), exit immediately the thread.
+ thread which called Py_Finalize(), this thread cannot continue.
This code path can be reached by a daemon thread after Py_Finalize()
completes. In this case, tstate is a dangling pointer: points to
- PyThreadState freed memory. */
- PyThread_exit_thread();
+ PyThreadState freed memory.
+
+ This used to call a *thread_exit API, but that was not safe as it
+ lacks stack unwinding and local variable destruction important to
+ C++. gh-87135: The best that can be done is to hang the thread as
+ the public APIs calling this have no error reporting mechanism (!).
+ */
+ PyThread_hang_thread();
}
assert(_PyThreadState_CheckConsistency(tstate));
@@ -342,7 +348,9 @@ take_gil(PyThreadState *tstate)
if (drop_requested) {
_Py_unset_eval_breaker_bit(holder_tstate, _PY_GIL_DROP_REQUEST_BIT);
}
- PyThread_exit_thread();
+ // gh-87135: hang the thread as *thread_exit() is not a safe
+ // API. It lacks stack unwind and local variable destruction.
+ PyThread_hang_thread();
}
assert(_PyThreadState_CheckConsistency(tstate));
@@ -383,7 +391,7 @@ take_gil(PyThreadState *tstate)
if (_PyThreadState_MustExit(tstate)) {
/* bpo-36475: If Py_Finalize() has been called and tstate is not
- the thread which called Py_Finalize(), exit immediately the
+ the thread which called Py_Finalize(), gh-87135: hang the
thread.
This code path can be reached by a daemon thread which was waiting
@@ -393,7 +401,7 @@ take_gil(PyThreadState *tstate)
/* tstate could be a dangling pointer, so don't pass it to
drop_gil(). */
drop_gil(interp, NULL, 1);
- PyThread_exit_thread();
+ PyThread_hang_thread();
}
assert(_PyThreadState_CheckConsistency(tstate));