Skip to content

Commit da6730c

Browse files
authored
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().
1 parent 4596666 commit da6730c

File tree

4 files changed

+25
-36
lines changed

4 files changed

+25
-36
lines changed

Include/internal/pycore_interpframe.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,11 @@ _PyFrame_SetStackPointer(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer)
189189
* Frames on the frame stack are incomplete until the
190190
* first RESUME instruction.
191191
* Frames owned by a generator are always complete.
192+
*
193+
* NOTE: We allow racy accesses to the instruction pointer
194+
* from other threads for sys._current_frames() and similar APIs.
192195
*/
193-
static inline bool
196+
static inline bool _Py_NO_SANITIZE_THREAD
194197
_PyFrame_IsIncomplete(_PyInterpreterFrame *frame)
195198
{
196199
if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) {

Include/pyport.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,27 +565,45 @@ extern "C" {
565565
# if __has_feature(memory_sanitizer)
566566
# if !defined(_Py_MEMORY_SANITIZER)
567567
# define _Py_MEMORY_SANITIZER
568+
# define _Py_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
568569
# endif
569570
# endif
570571
# if __has_feature(address_sanitizer)
571572
# if !defined(_Py_ADDRESS_SANITIZER)
572573
# define _Py_ADDRESS_SANITIZER
574+
# define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
573575
# endif
574576
# endif
575577
# if __has_feature(thread_sanitizer)
576578
# if !defined(_Py_THREAD_SANITIZER)
577579
# define _Py_THREAD_SANITIZER
580+
# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
578581
# endif
579582
# endif
580583
#elif defined(__GNUC__)
581584
# if defined(__SANITIZE_ADDRESS__)
582585
# define _Py_ADDRESS_SANITIZER
586+
# define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
583587
# endif
584588
# if defined(__SANITIZE_THREAD__)
585589
# define _Py_THREAD_SANITIZER
590+
# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
591+
# elif __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
592+
// TSAN is supported since GCC 5.1, but __SANITIZE_THREAD__ macro
593+
// is provided only since GCC 7.
594+
# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
586595
# endif
587596
#endif
588597

598+
#ifndef _Py_NO_SANITIZE_ADDRESS
599+
# define _Py_NO_SANITIZE_ADDRESS
600+
#endif
601+
#ifndef _Py_NO_SANITIZE_THREAD
602+
# define _Py_NO_SANITIZE_THREAD
603+
#endif
604+
#ifndef _Py_NO_SANITIZE_MEMORY
605+
# define _Py_NO_SANITIZE_MEMORY
606+
#endif
589607

590608
/* AIX has __bool__ redefined in it's system header file. */
591609
#if defined(_AIX) && defined(__bool__)

Objects/obmalloc.c

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -470,40 +470,6 @@ _PyMem_ArenaFree(void *Py_UNUSED(ctx), void *ptr,
470470
/*******************************************/
471471

472472

473-
#if defined(__has_feature) /* Clang */
474-
# if __has_feature(address_sanitizer) /* is ASAN enabled? */
475-
# define _Py_NO_SANITIZE_ADDRESS \
476-
__attribute__((no_sanitize("address")))
477-
# endif
478-
# if __has_feature(thread_sanitizer) /* is TSAN enabled? */
479-
# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
480-
# endif
481-
# if __has_feature(memory_sanitizer) /* is MSAN enabled? */
482-
# define _Py_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
483-
# endif
484-
#elif defined(__GNUC__)
485-
# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8+, is ASAN enabled? */
486-
# define _Py_NO_SANITIZE_ADDRESS \
487-
__attribute__((no_sanitize_address))
488-
# endif
489-
// TSAN is supported since GCC 5.1, but __SANITIZE_THREAD__ macro
490-
// is provided only since GCC 7.
491-
# if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
492-
# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
493-
# endif
494-
#endif
495-
496-
#ifndef _Py_NO_SANITIZE_ADDRESS
497-
# define _Py_NO_SANITIZE_ADDRESS
498-
#endif
499-
#ifndef _Py_NO_SANITIZE_THREAD
500-
# define _Py_NO_SANITIZE_THREAD
501-
#endif
502-
#ifndef _Py_NO_SANITIZE_MEMORY
503-
# define _Py_NO_SANITIZE_MEMORY
504-
#endif
505-
506-
507473
#define ALLOCATORS_MUTEX (_PyRuntime.allocators.mutex)
508474
#define _PyMem_Raw (_PyRuntime.allocators.standard.raw)
509475
#define _PyMem (_PyRuntime.allocators.standard.mem)

Python/frame.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame)
139139
return _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);
140140
}
141141

142-
int
142+
// NOTE: We allow racy accesses to the instruction pointer from other threads
143+
// for sys._current_frames() and similar APIs.
144+
int _Py_NO_SANITIZE_THREAD
143145
PyUnstable_InterpreterFrame_GetLine(_PyInterpreterFrame *frame)
144146
{
145147
int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);

0 commit comments

Comments
 (0)