python / python/cpython

Data race reading `_thread.RLock` recursion count in `repr()` under free-threading

Ouverte
#154,928 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

extension-modules type-bug
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

Description

Bug report

Bug description:

This is a follow-up to #153292 (data race in repr() of _thread.RLock), which was fixed by making rlock_repr read the lock's owner with an atomic load. The fix covered the owner (self->lock.thread) field, but rlock_repr still reads self->lock.level with a plain (non-atomic) load to compute the recursion count:

https://github.com/python/cpython/blob/22a6c51c94a4fde986b8964f1d36d5ec3ac20dcc/Modules/_threadmodule.c#L1287-L1305

self->lock.level is written by acquire / release / _acquire_restore, e.g.:

https://github.com/python/cpython/blob/22a6c51c94a4fde986b8964f1d36d5ec3ac20dcc/Modules/_threadmodule.c#L1198-L1210

So on a free-threaded build, repr(rlock) concurrent with acquire() or release() is still a data race, now on the level field rather than the owner field the earlier fix addressed.

Reproducer:

import _thread
from threading import Thread, Barrier

shared_rlock = _thread.RLock()
state = (5, _thread.get_ident())

def chain1_thread():
    for _ in range(20000):
        try:
            shared_rlock._acquire_restore(state)
        except Exception:
            pass

def chain2_thread():
    for _ in range(20000):
        try:
            repr(shared_rlock)
        except Exception:
            pass

N_C1 = 2
N_C2 = 4
barrier = Barrier(N_C1 + N_C2)

def _c1():
    barrier.wait()
    chain1_thread()

def _c2():
    barrier.wait()
    chain2_thread()

threads  = [Thread(target=_c1) for _ in range(N_C1)]
threads += [Thread(target=_c2) for _ in range(N_C2)]
for t in threads: t.start()
for t in threads: t.join()

TSAN Report :

==================
WARNING: ThreadSanitizer: data race (pid=655652)
  Read of size 8 at 0x7fffb6610540 by thread T6:
    #0 rlock_repr /cpython/./Modules/_threadmodule.c:1295:28 
    #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 write of size 8 at 0x7fffb6610540 by thread T1:
    #0 _thread_RLock__acquire_restore_impl /cpython/./Modules/_threadmodule.c:1210:22 
    #1 _thread_RLock__acquire_restore /cpython/./Modules/clinic/_threadmodule.c.h:537:20 
    #2 method_vectorcall_O /cpython/Objects/descrobject.c:476:24 
    #3 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 
    #4 PyObject_Vectorcall /cpython/Objects/call.c:327:12 
    #5 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:768:11 
    #6 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1906:35

SUMMARY: ThreadSanitizer: data race /cpython/./Modules/_threadmodule.c:1295:28 in rlock_repr
==================
CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-155381

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans Modules/_threadmodule.c, au niveau de rlock_repr et des implémentations de acquire, release et _acquire_restore décrites dans le rapport. Exécutez le reproducer fourni sur un build free-threaded avec ThreadSanitizer, puis examinez les tests associés et vérifiez que les appels concurrents à repr() et les mises à jour du verrou ne signalent plus de race.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
c, python
Domaine
operating-systems
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
35/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.