From 758178407de4d6f66ddfd1a3a580f8c480b27554 Mon Sep 17 00:00:00 2001 From: Matt Page <mpage@meta.com> Date: Wed, 24 Apr 2024 15:26:06 -0700 Subject: [PATCH 1/2] Fix race data race in `_Py_IsOwnedByCurrentThread()` We need to load `ob_tid` atomically; it may be set to zero in another thread without any intervening operations that establish a happens-before relationship. Using a relaxed load should be sufficient; we do not depend on the store publishing any other information. Examples: The load in `_Py_IsOwnedByCurrentThread()`: https://github.com/python/cpython/blob/4b10e209c76f9f36f8ae2e4d713b3a01591c1856/Include/object.h#L306 races with the store in `_Py_ExplicitMergeRefcount()` ([example](https://gist.github.com/mpage/0342ebce4b5b446689c41cdd72f16e61)): https://github.com/python/cpython/blob/4b10e209c76f9f36f8ae2e4d713b3a01591c1856/Objects/object.c#L421 and the store in `_Py_MergeZeroLocalRefcount()` ([example](https://gist.github.com/mpage/eaec11eda6e2d988ed0dd2bcfd6d606b)): https://github.com/python/cpython/blob/4b10e209c76f9f36f8ae2e4d713b3a01591c1856/Objects/object.c#L378 --- Include/object.h | 2 +- Tools/tsan/suppressions_free_threading.txt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Include/object.h b/Include/object.h index ffcacf1a3ef4ed..52317d397dd534 100644 --- a/Include/object.h +++ b/Include/object.h @@ -303,7 +303,7 @@ _Py_ThreadId(void) static inline Py_ALWAYS_INLINE int _Py_IsOwnedByCurrentThread(PyObject *ob) { - return ob->ob_tid == _Py_ThreadId(); + return _Py_atomic_load_uintptr_relaxed(&ob->ob_tid) == _Py_ThreadId(); } #endif diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 1408103ba80f96..94be478dc75044 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -15,7 +15,6 @@ race:_add_to_weak_set race:_in_weak_set race:_mi_heap_delayed_free_partial race:_Py_IsImmortal -race:_Py_IsOwnedByCurrentThread race:_PyEval_EvalFrameDefault race:_PyFunction_SetVersion race:_PyImport_AcquireLock From 425aa3f4bc17bbc52cad0924442999c9ec811f57 Mon Sep 17 00:00:00 2001 From: Matt Page <mpage@meta.com> Date: Thu, 25 Apr 2024 13:10:40 -0700 Subject: [PATCH 2/2] Only use atomics with TSAN --- Include/object.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Include/object.h b/Include/object.h index 52317d397dd534..505d31629104a9 100644 --- a/Include/object.h +++ b/Include/object.h @@ -303,7 +303,11 @@ _Py_ThreadId(void) static inline Py_ALWAYS_INLINE int _Py_IsOwnedByCurrentThread(PyObject *ob) { +#ifdef _Py_THREAD_SANITIZER return _Py_atomic_load_uintptr_relaxed(&ob->ob_tid) == _Py_ThreadId(); +#else + return ob->ob_tid == _Py_ThreadId(); +#endif } #endif