https://github.com/python/cpython/commit/80b752285d861ca5d092d02f8e2117b88b54d6d7
commit: 80b752285d861ca5d092d02f8e2117b88b54d6d7
branch: 3.13
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2025-11-27T17:55:31+01:00
summary:
[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 d5d9e89dde9843a61b46872b1914c5b75ad05998)
files:
M Include/internal/pycore_pymem.h
M Python/traceback.c
diff --git a/Include/internal/pycore_pymem.h b/Include/internal/pycore_pymem.h
index 79c43a0768b705..76d58d1d251d27 100644
--- a/Include/internal/pycore_pymem.h
+++ b/Include/internal/pycore_pymem.h
@@ -106,6 +106,27 @@ static inline int _PyMem_IsPtrFreed(const void *ptr)
#endif
}
+// Similar to _PyMem_IsPtrFreed() but expects an 'unsigned long' instead of a
+// pointer.
+static inline int _PyMem_IsULongFreed(unsigned long value)
+{
+#if SIZEOF_LONG == 8
+ return (value == 0
+ || value == (unsigned long)0xCDCDCDCDCDCDCDCD
+ || value == (unsigned long)0xDDDDDDDDDDDDDDDD
+ || value == (unsigned long)0xFDFDFDFDFDFDFDFD
+ || value == (unsigned long)0xFFFFFFFFFFFFFFFF);
+#elif SIZEOF_LONG == 4
+ return (value == 0
+ || value == (unsigned long)0xCDCDCDCD
+ || value == (unsigned long)0xDDDDDDDD
+ || value == (unsigned long)0xFDFDFDFD
+ || value == (unsigned long)0xFFFFFFFF);
+#else
+# error "unknown long size"
+#endif
+}
+
extern int _PyMem_GetAllocatorName(
const char *name,
PyMemAllocatorName *allocator);
diff --git a/Python/traceback.c b/Python/traceback.c
index 7e7739682794ac..f98408337164dc 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -960,6 +960,9 @@ tstate_is_freed(PyThreadState *tstate)
if (_PyMem_IsPtrFreed(tstate->interp)) {
return 1;
}
+ if (_PyMem_IsULongFreed(tstate->thread_id)) {
+ return 1;
+ }
return 0;
}
@@ -979,7 +982,7 @@ dump_traceback(int fd, PyThreadState *tstate, int
write_header)
}
if (tstate_is_freed(tstate)) {
- PUTS(fd, " <tstate is freed>\n");
+ PUTS(fd, " <freed thread state>\n");
return;
}
@@ -1004,12 +1007,16 @@ dump_traceback(int fd, PyThreadState *tstate, int
write_header)
PUTS(fd, " <freed frame>\n");
break;
}
+ // Read frame->previous early since memory can be freed during
+ // dump_frame()
+ _PyInterpreterFrame *previous = frame->previous;
+
if (dump_frame(fd, frame) < 0) {
PUTS(fd, " <invalid frame>\n");
break;
}
- frame = frame->previous;
+ frame = previous;
if (frame == NULL) {
break;
}
@@ -1100,7 +1107,6 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState
*interp,
return "unable to get the thread head state";
/* Dump the traceback of each thread */
- tstate = PyInterpreterState_ThreadHead(interp);
unsigned int nthreads = 0;
_Py_BEGIN_SUPPRESS_IPH
do
@@ -1111,11 +1117,18 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState
*interp,
PUTS(fd, "...\n");
break;
}
+
+ if (tstate_is_freed(tstate)) {
+ PUTS(fd, "<freed thread state>\n");
+ break;
+ }
+
write_thread_id(fd, tstate, tstate == current_tstate);
if (tstate == current_tstate && tstate->interp->gc.collecting) {
PUTS(fd, " Garbage-collecting\n");
}
dump_traceback(fd, tstate, 0);
+
tstate = PyThreadState_Next(tstate);
nthreads++;
} while (tstate != NULL);
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]