[3.11] gh-110052: Fix faulthandler for freed tstate (#110069) (#110072)

gh-110052: Fix faulthandler for freed tstate (#110069)

faulthandler now detected freed interp and freed tstate, and no
longer dereference them.

Backport to 3.11: add pycore_pymem.h include to traceback.c.

(cherry picked from commit 2e37a38bcb)
This commit is contained in:
Victor Stinner 2023-09-29 04:43:28 +02:00 committed by GitHub
parent efe83ad276
commit 615d7fc34a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 12 deletions

View file

@ -231,7 +231,6 @@ faulthandler_dump_traceback(int fd, int all_threads,
PyInterpreterState *interp) PyInterpreterState *interp)
{ {
static volatile int reentrant = 0; static volatile int reentrant = 0;
PyThreadState *tstate;
if (reentrant) if (reentrant)
return; return;
@ -246,7 +245,7 @@ faulthandler_dump_traceback(int fd, int all_threads,
fault if the thread released the GIL, and so this function cannot be fault if the thread released the GIL, and so this function cannot be
used. Read the thread specific storage (TSS) instead: call used. Read the thread specific storage (TSS) instead: call
PyGILState_GetThisThreadState(). */ PyGILState_GetThisThreadState(). */
tstate = PyGILState_GetThisThreadState(); PyThreadState *tstate = PyGILState_GetThisThreadState();
if (all_threads) { if (all_threads) {
(void)_Py_DumpTracebackThreads(fd, NULL, tstate); (void)_Py_DumpTracebackThreads(fd, NULL, tstate);

View file

@ -12,6 +12,7 @@
#include "pycore_parser.h" // _PyParser_ASTFromString #include "pycore_parser.h" // _PyParser_ASTFromString
#include "pycore_pyarena.h" // _PyArena_Free() #include "pycore_pyarena.h" // _PyArena_Free()
#include "pycore_pyerrors.h" // _PyErr_Fetch() #include "pycore_pyerrors.h" // _PyErr_Fetch()
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_traceback.h" // EXCEPTION_TB_HEADER #include "pycore_traceback.h" // EXCEPTION_TB_HEADER
@ -1234,23 +1235,45 @@ dump_frame(int fd, _PyInterpreterFrame *frame)
PUTS(fd, "\n"); PUTS(fd, "\n");
} }
static int
tstate_is_freed(PyThreadState *tstate)
{
if (_PyMem_IsPtrFreed(tstate)) {
return 1;
}
if (_PyMem_IsPtrFreed(tstate->interp)) {
return 1;
}
return 0;
}
static int
interp_is_freed(PyInterpreterState *interp)
{
return _PyMem_IsPtrFreed(interp);
}
static void static void
dump_traceback(int fd, PyThreadState *tstate, int write_header) dump_traceback(int fd, PyThreadState *tstate, int write_header)
{ {
_PyInterpreterFrame *frame;
unsigned int depth;
if (write_header) { if (write_header) {
PUTS(fd, "Stack (most recent call first):\n"); PUTS(fd, "Stack (most recent call first):\n");
} }
frame = tstate->cframe->current_frame; if (tstate_is_freed(tstate)) {
PUTS(fd, " <tstate is freed>\n");
return;
}
_PyInterpreterFrame *frame = tstate->cframe->current_frame;
if (frame == NULL) { if (frame == NULL) {
PUTS(fd, " <no Python frame>\n"); PUTS(fd, " <no Python frame>\n");
return; return;
} }
depth = 0; unsigned int depth = 0;
while (1) { while (1) {
if (MAX_FRAME_DEPTH <= depth) { if (MAX_FRAME_DEPTH <= depth) {
PUTS(fd, " ...\n"); PUTS(fd, " ...\n");
@ -1305,9 +1328,6 @@ const char*
_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
PyThreadState *current_tstate) PyThreadState *current_tstate)
{ {
PyThreadState *tstate;
unsigned int nthreads;
if (current_tstate == NULL) { if (current_tstate == NULL) {
/* _Py_DumpTracebackThreads() is called from signal handlers by /* _Py_DumpTracebackThreads() is called from signal handlers by
faulthandler. faulthandler.
@ -1323,6 +1343,10 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
current_tstate = PyGILState_GetThisThreadState(); current_tstate = PyGILState_GetThisThreadState();
} }
if (current_tstate != NULL && tstate_is_freed(current_tstate)) {
return "tstate is freed";
}
if (interp == NULL) { if (interp == NULL) {
if (current_tstate == NULL) { if (current_tstate == NULL) {
interp = _PyGILState_GetInterpreterStateUnsafe(); interp = _PyGILState_GetInterpreterStateUnsafe();
@ -1337,14 +1361,18 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
} }
assert(interp != NULL); assert(interp != NULL);
if (interp_is_freed(interp)) {
return "interp is freed";
}
/* Get the current interpreter from the current thread */ /* Get the current interpreter from the current thread */
tstate = PyInterpreterState_ThreadHead(interp); PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
if (tstate == NULL) if (tstate == NULL)
return "unable to get the thread head state"; return "unable to get the thread head state";
/* Dump the traceback of each thread */ /* Dump the traceback of each thread */
tstate = PyInterpreterState_ThreadHead(interp); tstate = PyInterpreterState_ThreadHead(interp);
nthreads = 0; unsigned int nthreads = 0;
_Py_BEGIN_SUPPRESS_IPH _Py_BEGIN_SUPPRESS_IPH
do do
{ {