https://github.com/python/cpython/commit/da6730caa5023bd351a3f2a1b64b174c8e609489
commit: da6730caa5023bd351a3f2a1b64b174c8e609489
branch: main
author: Sam Gross <[email protected]>
committer: colesbury <[email protected]>
date: 2025-03-24T09:49:39-04:00
summary:
gh-128421: Avoid TSAN warnings in `sys._current_frames()` (gh-131548)
This tells TSAN not to sanitize `PyUnstable_InterpreterFrame_GetLine()`.
There's a possible data race on the access to the frame's `instr_ptr`
if the frame is currently executing. We don't really care about the
race. In theory, we could use relaxed atomics for every access to
`instr_ptr`, but that would create more code churn and current compilers
are overly conservative with optimizations around relaxed atomic
accesses.
We also don't sanitize `_PyFrame_IsIncomplete()` because it accesses
`instr_ptr` and is called from assertions within PyFrame_GetCode().
files:
M Include/internal/pycore_interpframe.h
M Include/pyport.h
M Objects/obmalloc.c
M Python/frame.c
diff --git a/Include/internal/pycore_interpframe.h
b/Include/internal/pycore_interpframe.h
index 41acd877aee873..097da4edee1aaf 100644
--- a/Include/internal/pycore_interpframe.h
+++ b/Include/internal/pycore_interpframe.h
@@ -189,8 +189,11 @@ _PyFrame_SetStackPointer(_PyInterpreterFrame *frame,
_PyStackRef *stack_pointer)
* Frames on the frame stack are incomplete until the
* first RESUME instruction.
* Frames owned by a generator are always complete.
+ *
+ * NOTE: We allow racy accesses to the instruction pointer
+ * from other threads for sys._current_frames() and similar APIs.
*/
-static inline bool
+static inline bool _Py_NO_SANITIZE_THREAD
_PyFrame_IsIncomplete(_PyInterpreterFrame *frame)
{
if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) {
diff --git a/Include/pyport.h b/Include/pyport.h
index aabd094df54a74..e7162f4d9bf6b0 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -565,27 +565,45 @@ extern "C" {
# if __has_feature(memory_sanitizer)
# if !defined(_Py_MEMORY_SANITIZER)
# define _Py_MEMORY_SANITIZER
+# define _Py_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
# endif
# endif
# if __has_feature(address_sanitizer)
# if !defined(_Py_ADDRESS_SANITIZER)
# define _Py_ADDRESS_SANITIZER
+# define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
# endif
# endif
# if __has_feature(thread_sanitizer)
# if !defined(_Py_THREAD_SANITIZER)
# define _Py_THREAD_SANITIZER
+# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
# endif
# endif
#elif defined(__GNUC__)
# if defined(__SANITIZE_ADDRESS__)
# define _Py_ADDRESS_SANITIZER
+# define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
# endif
# if defined(__SANITIZE_THREAD__)
# define _Py_THREAD_SANITIZER
+# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
+# elif __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
+ // TSAN is supported since GCC 5.1, but __SANITIZE_THREAD__ macro
+ // is provided only since GCC 7.
+# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
# endif
#endif
+#ifndef _Py_NO_SANITIZE_ADDRESS
+# define _Py_NO_SANITIZE_ADDRESS
+#endif
+#ifndef _Py_NO_SANITIZE_THREAD
+# define _Py_NO_SANITIZE_THREAD
+#endif
+#ifndef _Py_NO_SANITIZE_MEMORY
+# define _Py_NO_SANITIZE_MEMORY
+#endif
/* AIX has __bool__ redefined in it's system header file. */
#if defined(_AIX) && defined(__bool__)
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
index be85a20c00dfea..1285398142933a 100644
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -470,40 +470,6 @@ _PyMem_ArenaFree(void *Py_UNUSED(ctx), void *ptr,
/*******************************************/
-#if defined(__has_feature) /* Clang */
-# if __has_feature(address_sanitizer) /* is ASAN enabled? */
-# define _Py_NO_SANITIZE_ADDRESS \
- __attribute__((no_sanitize("address")))
-# endif
-# if __has_feature(thread_sanitizer) /* is TSAN enabled? */
-# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
-# endif
-# if __has_feature(memory_sanitizer) /* is MSAN enabled? */
-# define _Py_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
-# endif
-#elif defined(__GNUC__)
-# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8+, is ASAN enabled? */
-# define _Py_NO_SANITIZE_ADDRESS \
- __attribute__((no_sanitize_address))
-# endif
- // TSAN is supported since GCC 5.1, but __SANITIZE_THREAD__ macro
- // is provided only since GCC 7.
-# if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
-# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
-# endif
-#endif
-
-#ifndef _Py_NO_SANITIZE_ADDRESS
-# define _Py_NO_SANITIZE_ADDRESS
-#endif
-#ifndef _Py_NO_SANITIZE_THREAD
-# define _Py_NO_SANITIZE_THREAD
-#endif
-#ifndef _Py_NO_SANITIZE_MEMORY
-# define _Py_NO_SANITIZE_MEMORY
-#endif
-
-
#define ALLOCATORS_MUTEX (_PyRuntime.allocators.mutex)
#define _PyMem_Raw (_PyRuntime.allocators.standard.raw)
#define _PyMem (_PyRuntime.allocators.standard.mem)
diff --git a/Python/frame.c b/Python/frame.c
index b59cb4bbb5536e..558f92055bbfc1 100644
--- a/Python/frame.c
+++ b/Python/frame.c
@@ -139,7 +139,9 @@ PyUnstable_InterpreterFrame_GetLasti(struct
_PyInterpreterFrame *frame)
return _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);
}
-int
+// NOTE: We allow racy accesses to the instruction pointer from other threads
+// for sys._current_frames() and similar APIs.
+int _Py_NO_SANITIZE_THREAD
PyUnstable_InterpreterFrame_GetLine(_PyInterpreterFrame *frame)
{
int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);
_______________________________________________
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]