Skip to content

Commit 2e37a38

Browse files
authored
gh-110052: Fix faulthandler for freed tstate (#110069)
faulthandler now detected freed interp and freed tstate, and no longer dereference them.
1 parent 235aacd commit 2e37a38

File tree

2 files changed

+38
-12
lines changed

2 files changed

+38
-12
lines changed

Modules/faulthandler.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ faulthandler_dump_traceback(int fd, int all_threads,
174174
PyInterpreterState *interp)
175175
{
176176
static volatile int reentrant = 0;
177-
PyThreadState *tstate;
178177

179178
if (reentrant)
180179
return;
@@ -189,7 +188,7 @@ faulthandler_dump_traceback(int fd, int all_threads,
189188
fault if the thread released the GIL, and so this function cannot be
190189
used. Read the thread specific storage (TSS) instead: call
191190
PyGILState_GetThisThreadState(). */
192-
tstate = PyGILState_GetThisThreadState();
191+
PyThreadState *tstate = PyGILState_GetThisThreadState();
193192

194193
if (all_threads) {
195194
(void)_Py_DumpTracebackThreads(fd, NULL, tstate);

Python/traceback.c

+37-10
Original file line numberDiff line numberDiff line change
@@ -1215,23 +1215,45 @@ dump_frame(int fd, _PyInterpreterFrame *frame)
12151215
PUTS(fd, "\n");
12161216
}
12171217

1218+
static int
1219+
tstate_is_freed(PyThreadState *tstate)
1220+
{
1221+
if (_PyMem_IsPtrFreed(tstate)) {
1222+
return 1;
1223+
}
1224+
if (_PyMem_IsPtrFreed(tstate->interp)) {
1225+
return 1;
1226+
}
1227+
return 0;
1228+
}
1229+
1230+
1231+
static int
1232+
interp_is_freed(PyInterpreterState *interp)
1233+
{
1234+
return _PyMem_IsPtrFreed(interp);
1235+
}
1236+
1237+
12181238
static void
12191239
dump_traceback(int fd, PyThreadState *tstate, int write_header)
12201240
{
1221-
_PyInterpreterFrame *frame;
1222-
unsigned int depth;
1223-
12241241
if (write_header) {
12251242
PUTS(fd, "Stack (most recent call first):\n");
12261243
}
12271244

1228-
frame = tstate->current_frame;
1245+
if (tstate_is_freed(tstate)) {
1246+
PUTS(fd, " <tstate is freed>\n");
1247+
return;
1248+
}
1249+
1250+
_PyInterpreterFrame *frame = tstate->current_frame;
12291251
if (frame == NULL) {
12301252
PUTS(fd, " <no Python frame>\n");
12311253
return;
12321254
}
12331255

1234-
depth = 0;
1256+
unsigned int depth = 0;
12351257
while (1) {
12361258
if (MAX_FRAME_DEPTH <= depth) {
12371259
PUTS(fd, " ...\n");
@@ -1295,9 +1317,6 @@ const char*
12951317
_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
12961318
PyThreadState *current_tstate)
12971319
{
1298-
PyThreadState *tstate;
1299-
unsigned int nthreads;
1300-
13011320
if (current_tstate == NULL) {
13021321
/* _Py_DumpTracebackThreads() is called from signal handlers by
13031322
faulthandler.
@@ -1313,6 +1332,10 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
13131332
current_tstate = PyGILState_GetThisThreadState();
13141333
}
13151334

1335+
if (current_tstate != NULL && tstate_is_freed(current_tstate)) {
1336+
return "tstate is freed";
1337+
}
1338+
13161339
if (interp == NULL) {
13171340
if (current_tstate == NULL) {
13181341
interp = _PyGILState_GetInterpreterStateUnsafe();
@@ -1327,14 +1350,18 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
13271350
}
13281351
assert(interp != NULL);
13291352

1353+
if (interp_is_freed(interp)) {
1354+
return "interp is freed";
1355+
}
1356+
13301357
/* Get the current interpreter from the current thread */
1331-
tstate = PyInterpreterState_ThreadHead(interp);
1358+
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
13321359
if (tstate == NULL)
13331360
return "unable to get the thread head state";
13341361

13351362
/* Dump the traceback of each thread */
13361363
tstate = PyInterpreterState_ThreadHead(interp);
1337-
nthreads = 0;
1364+
unsigned int nthreads = 0;
13381365
_Py_BEGIN_SUPPRESS_IPH
13391366
do
13401367
{

0 commit comments

Comments
 (0)