From d815525bbcdb977d6df95121743eadee924afe4b Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 23 Dec 2025 11:50:06 -0500 Subject: [PATCH] gh-143108: Don't TSan instrument faulthandler.c The dumping of tracebacks has data races and that's okay (it's best effort). --- Include/internal/pycore_interpframe.h | 4 ++-- Python/traceback.c | 12 ++++++------ Tools/tsan/suppressions_free_threading.txt | 6 ------ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_interpframe.h b/Include/internal/pycore_interpframe.h index 8949d6cc2fc4bb..2e9fbd39b276b1 100644 --- a/Include/internal/pycore_interpframe.h +++ b/Include/internal/pycore_interpframe.h @@ -27,7 +27,7 @@ static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) { // Similar to _PyFrame_GetCode(), but return NULL if the frame is invalid or // freed. Used by dump_frame() in Python/traceback.c. The function uses // heuristics to detect freed memory, it's not 100% reliable. -static inline PyCodeObject* +static inline PyCodeObject* _Py_NO_SANITIZE_THREAD _PyFrame_SafeGetCode(_PyInterpreterFrame *f) { // globals and builtins may be NULL on a legit frame, but it's unlikely. @@ -70,7 +70,7 @@ _PyFrame_GetBytecode(_PyInterpreterFrame *f) // Similar to PyUnstable_InterpreterFrame_GetLasti(), but return NULL if the // frame is invalid or freed. Used by dump_frame() in Python/traceback.c. The // function uses heuristics to detect freed memory, it's not 100% reliable. -static inline int +static inline int _Py_NO_SANITIZE_THREAD _PyFrame_SafeGetLasti(struct _PyInterpreterFrame *f) { // Code based on _PyFrame_GetBytecode() but replace _PyFrame_GetCode() diff --git a/Python/traceback.c b/Python/traceback.c index 264f034dea7fa5..40e19c7cc82075 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -1035,7 +1035,7 @@ _Py_DumpWideString(int fd, wchar_t *str) Return 0 on success. Return -1 if the frame is invalid. */ -static int +static int _Py_NO_SANITIZE_THREAD dump_frame(int fd, _PyInterpreterFrame *frame) { if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { @@ -1088,7 +1088,7 @@ dump_frame(int fd, _PyInterpreterFrame *frame) return res; } -static int +static int _Py_NO_SANITIZE_THREAD tstate_is_freed(PyThreadState *tstate) { if (_PyMem_IsPtrFreed(tstate)) { @@ -1104,14 +1104,14 @@ tstate_is_freed(PyThreadState *tstate) } -static int +static int _Py_NO_SANITIZE_THREAD interp_is_freed(PyInterpreterState *interp) { return _PyMem_IsPtrFreed(interp); } -static void +static void _Py_NO_SANITIZE_THREAD dump_traceback(int fd, PyThreadState *tstate, int write_header) { if (write_header) { @@ -1263,7 +1263,7 @@ write_thread_id(int fd, PyThreadState *tstate, int is_current) The caller is responsible to call PyErr_CheckSignals() to call Python signal handlers if signals were received. */ -const char* +const char* _Py_NO_SANITIZE_THREAD _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, PyThreadState *current_tstate) { @@ -1332,7 +1332,7 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, } dump_traceback(fd, tstate, 0); - tstate = PyThreadState_Next(tstate); + tstate = tstate->next; nthreads++; } while (tstate != NULL); _Py_END_SUPPRESS_IPH diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index e8b1501c34bfc1..a3e1e54284f0ae 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -12,13 +12,7 @@ # These warnings trigger directly in a CPython function. -race_top:dump_traceback -race_top:fatal_error -race_top:_PyFrame_GetCode -race_top:_PyFrame_Initialize race_top:_PyObject_TryGetInstanceAttribute -race_top:PyUnstable_InterpreterFrame_GetLine -race_top:write_thread_id # https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40 thread:pthread_create