Skip to content

Commit 80b7522

Browse files
authored
[3.13] gh-116008: Detect freed thread state in faulthandler (#141988) (#142017)
gh-116008: Detect freed thread state in faulthandler (#141988) Add _PyMem_IsULongFreed() function. (cherry picked from commit d5d9e89)
1 parent 9756d8c commit 80b7522

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

Include/internal/pycore_pymem.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,27 @@ static inline int _PyMem_IsPtrFreed(const void *ptr)
106106
#endif
107107
}
108108

109+
// Similar to _PyMem_IsPtrFreed() but expects an 'unsigned long' instead of a
110+
// pointer.
111+
static inline int _PyMem_IsULongFreed(unsigned long value)
112+
{
113+
#if SIZEOF_LONG == 8
114+
return (value == 0
115+
|| value == (unsigned long)0xCDCDCDCDCDCDCDCD
116+
|| value == (unsigned long)0xDDDDDDDDDDDDDDDD
117+
|| value == (unsigned long)0xFDFDFDFDFDFDFDFD
118+
|| value == (unsigned long)0xFFFFFFFFFFFFFFFF);
119+
#elif SIZEOF_LONG == 4
120+
return (value == 0
121+
|| value == (unsigned long)0xCDCDCDCD
122+
|| value == (unsigned long)0xDDDDDDDD
123+
|| value == (unsigned long)0xFDFDFDFD
124+
|| value == (unsigned long)0xFFFFFFFF);
125+
#else
126+
# error "unknown long size"
127+
#endif
128+
}
129+
109130
extern int _PyMem_GetAllocatorName(
110131
const char *name,
111132
PyMemAllocatorName *allocator);

Python/traceback.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,9 @@ tstate_is_freed(PyThreadState *tstate)
960960
if (_PyMem_IsPtrFreed(tstate->interp)) {
961961
return 1;
962962
}
963+
if (_PyMem_IsULongFreed(tstate->thread_id)) {
964+
return 1;
965+
}
963966
return 0;
964967
}
965968

@@ -979,7 +982,7 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
979982
}
980983

981984
if (tstate_is_freed(tstate)) {
982-
PUTS(fd, " <tstate is freed>\n");
985+
PUTS(fd, " <freed thread state>\n");
983986
return;
984987
}
985988

@@ -1004,12 +1007,16 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
10041007
PUTS(fd, " <freed frame>\n");
10051008
break;
10061009
}
1010+
// Read frame->previous early since memory can be freed during
1011+
// dump_frame()
1012+
_PyInterpreterFrame *previous = frame->previous;
1013+
10071014
if (dump_frame(fd, frame) < 0) {
10081015
PUTS(fd, " <invalid frame>\n");
10091016
break;
10101017
}
10111018

1012-
frame = frame->previous;
1019+
frame = previous;
10131020
if (frame == NULL) {
10141021
break;
10151022
}
@@ -1100,7 +1107,6 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
11001107
return "unable to get the thread head state";
11011108

11021109
/* Dump the traceback of each thread */
1103-
tstate = PyInterpreterState_ThreadHead(interp);
11041110
unsigned int nthreads = 0;
11051111
_Py_BEGIN_SUPPRESS_IPH
11061112
do
@@ -1111,11 +1117,18 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
11111117
PUTS(fd, "...\n");
11121118
break;
11131119
}
1120+
1121+
if (tstate_is_freed(tstate)) {
1122+
PUTS(fd, "<freed thread state>\n");
1123+
break;
1124+
}
1125+
11141126
write_thread_id(fd, tstate, tstate == current_tstate);
11151127
if (tstate == current_tstate && tstate->interp->gc.collecting) {
11161128
PUTS(fd, " Garbage-collecting\n");
11171129
}
11181130
dump_traceback(fd, tstate, 0);
1131+
11191132
tstate = PyThreadState_Next(tstate);
11201133
nthreads++;
11211134
} while (tstate != NULL);

0 commit comments

Comments
 (0)