Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pythongh-110052: Fix faulthandler for freed tstate
Browse files Browse the repository at this point in the history
faulthandler now detected freed interp and freed tstate, and no
longer dereference them.
vstinner committed Sep 29, 2023
1 parent 235aacd commit e4b3bc8
Showing 3 changed files with 41 additions and 12 deletions.
3 changes: 3 additions & 0 deletions Lib/test/test_faulthandler.py
Original file line number Diff line number Diff line change
@@ -650,6 +650,9 @@ def func(timeout, repeat, cancel, file, loops):
filename=filename,
fd=fd,
)
print("--")
print(code)
print("--")
trace, exitcode = self.get_output(code, filename)
trace = '\n'.join(trace)

3 changes: 1 addition & 2 deletions Modules/faulthandler.c
Original file line number Diff line number Diff line change
@@ -174,7 +174,6 @@ faulthandler_dump_traceback(int fd, int all_threads,
PyInterpreterState *interp)
{
static volatile int reentrant = 0;
PyThreadState *tstate;

if (reentrant)
return;
@@ -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);
47 changes: 37 additions & 10 deletions Python/traceback.c
Original file line number Diff line number Diff line change
@@ -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");
@@ -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.
@@ -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();
@@ -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
{

0 comments on commit e4b3bc8

Please sign in to comment.