Bug report
Bug description:
In the free-threaded build, rlock_repr reads the recursive lock's owner with plain loads to build the repr string, while acquire/release/_acquire_restore write the owner with atomic stores.
|
static PyObject * |
|
rlock_repr(PyObject *op) |
|
{ |
|
rlockobject *self = rlockobject_CAST(op); |
|
PyThread_ident_t owner = self->lock.thread; |
|
int locked = rlock_locked_impl(self); |
The reader side is not atomic, so a repr() concurrent with acquire() is a data race on the owner field.
Reproducer:
import _thread
from threading import Thread
r = _thread.RLock()
def thread1():
for _ in range(20000):
try:
repr(r)
except Exception:
pass
def thread2():
for _ in range(20000):
r.acquire()
r.release()
threads = [Thread(target=thread1) for _ in range(6)]
threads += [Thread(target=thread2) for _ in range(2)]
for t in threads: t.start()
for t in threads: t.join()
TSAN Report:
==================
WARNING: ThreadSanitizer: data race (pid=40273)
Read of size 8 at 0x7fffb6600f38 by thread T1:
#0 rlock_repr /cpython/./Modules/_threadmodule.c:1291:41
#1 PyObject_Repr /cpython/Objects/object.c:784:11
#2 builtin_repr /cpython/Python/bltinmodule.c:2677:12
#3 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2712:35
...
Previous atomic write of size 8 at 0x7fffb6600f38 by thread T7:
#0 _Py_atomic_store_ullong_relaxed /cpython/./Include/cpython/pyatomic_gcc.h:518:3
#1 _PyRecursiveMutex_LockTimed /cpython/Python/lock.c:439:9
#2 _thread_RLock_acquire_impl /cpython/./Modules/_threadmodule.c:1102:22
#3 _thread_RLock_acquire /cpython/./Modules/clinic/_threadmodule.c.h:418:20
#4 _PyCallMethodDescriptorFastWithKeywords_StackRef /cpython/Python/ceval.c:883:11
#5 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:4205:35
...
SUMMARY: ThreadSanitizer: data race /cpython/./Modules/_threadmodule.c:1291:41 in rlock_repr
==================
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
Bug report
Bug description:
In the free-threaded build,
rlock_reprreads the recursive lock's owner with plain loads to build the repr string, whileacquire/release/_acquire_restorewrite the owner with atomic stores.cpython/Modules/_threadmodule.c
Lines 1287 to 1292 in a74280e
The reader side is not atomic, so a
repr()concurrent withacquire()is a data race on the owner field.Reproducer:
TSAN Report:
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
threading.RLock.__repr__in FT builds #153299threading.RLock.__repr__in FT builds (GH-153299) #153314threading.RLock.__repr__in FT builds (GH-153299) #153315