Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
gh-110052: Fix faulthandler for freed tstate (#110069)
faulthandler now detected freed interp and freed tstate, and no
longer dereference them.

(cherry picked from commit 2e37a38)
  • Loading branch information
vstinner committed Sep 29, 2023
commit faf7d4a2abfee4f78f8393a4ad47a4ab07813676
3 changes: 1 addition & 2 deletions Modules/faulthandler.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ faulthandler_dump_traceback(int fd, int all_threads,
PyInterpreterState *interp)
{
static volatile int reentrant = 0;
PyThreadState *tstate;

if (reentrant)
return;
Expand All @@ -191,7 +190,7 @@ faulthandler_dump_traceback(int fd, int all_threads,
fault if the thread released the GIL, and so this function cannot be
used. Read the thread specific storage (TSS) instead: call
PyGILState_GetThisThreadState(). */
tstate = PyGILState_GetThisThreadState();
PyThreadState *tstate = PyGILState_GetThisThreadState();

if (all_threads) {
(void)_Py_DumpTracebackThreads(fd, NULL, tstate);
Expand Down
47 changes: 37 additions & 10 deletions Python/traceback.c
Original file line number Diff line number Diff line change
Expand Up @@ -1218,23 +1218,45 @@ dump_frame(int fd, _PyInterpreterFrame *frame)
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
dump_traceback(int fd, PyThreadState *tstate, int write_header)
{
_PyInterpreterFrame *frame;
unsigned int depth;

if (write_header) {
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) {
PUTS(fd, " <no Python frame>\n");
return;
}

depth = 0;
unsigned int depth = 0;
while (1) {
if (MAX_FRAME_DEPTH <= depth) {
PUTS(fd, " ...\n");
Expand Down Expand Up @@ -1298,9 +1320,6 @@ const char*
_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
PyThreadState *current_tstate)
{
PyThreadState *tstate;
unsigned int nthreads;

if (current_tstate == NULL) {
/* _Py_DumpTracebackThreads() is called from signal handlers by
faulthandler.
Expand All @@ -1316,6 +1335,10 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
current_tstate = PyGILState_GetThisThreadState();
}

if (current_tstate != NULL && tstate_is_freed(current_tstate)) {
return "tstate is freed";
}

if (interp == NULL) {
if (current_tstate == NULL) {
interp = _PyGILState_GetInterpreterStateUnsafe();
Expand All @@ -1330,14 +1353,18 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
}
assert(interp != NULL);

if (interp_is_freed(interp)) {
return "interp is freed";
}

/* Get the current interpreter from the current thread */
tstate = PyInterpreterState_ThreadHead(interp);
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
if (tstate == NULL)
return "unable to get the thread head state";

/* Dump the traceback of each thread */
tstate = PyInterpreterState_ThreadHead(interp);
nthreads = 0;
unsigned int nthreads = 0;
_Py_BEGIN_SUPPRESS_IPH
do
{
Expand Down