Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-110052: Fix faulthandler for freed tstate #110069

Merged
merged 1 commit into from
Sep 29, 2023
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
3 changes: 1 addition & 2 deletions Modules/faulthandler.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ faulthandler_dump_traceback(int fd, int all_threads,
PyInterpreterState *interp)
{
static volatile int reentrant = 0;
PyThreadState *tstate;

if (reentrant)
return;
Expand All @@ -189,7 +188,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 @@ -1215,23 +1215,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->current_frame;
if (tstate_is_freed(tstate)) {
PUTS(fd, " <tstate is freed>\n");
return;
}

_PyInterpreterFrame *frame = tstate->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 @@ -1295,9 +1317,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 @@ -1313,6 +1332,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 @@ -1327,14 +1350,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