python / python/cpython

Data race between `sys._current_exceptions()` and a concurrently attaching thread

Ouverte
#152,741 2 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

extension-modules topic-free-threading 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 description:

In the free-threaded build, sys._current_exceptions() iterates every thread state under stop-the-world + HEAD_LOCK and reads t->thread_id.

https://github.com/python/cpython/blob/ecdef1773006529b0fea6639d0effeecbb41679c/Python/pystate.c#L2808-L2817

But a newly started thread sets tstate->thread_id in bind_tstate() without holding HEAD_LOCK. Its PyThreadState is already on the interpreter's thread list by this point.

https://github.com/python/cpython/blob/ecdef1773006529b0fea6639d0effeecbb41679c/Python/pystate.c#L164-L178

A thread that is still attaching is not yet stop-the-world-stoppable. _PyEval_StopTheWorldAll() therefore does not pause it, and its unlocked write to tstate->thread_id races the stop-the-world reader in _PyThread_CurrentExceptions.

Reproducer:

import sys
import threading

stop = threading.Event()

def reader():                      # keep stop-the-world almost always active
    for _ in range(20000):
        sys._current_exceptions()

def churn():                       # continuously attach new threads (bind_tstate)
    for _ in range(20000):
        t = threading.Thread(target=lambda: None)
        t.start()
        t.join()

readers  = [threading.Thread(target=reader) for _ in range(8)]
churners = [threading.Thread(target=churn)  for _ in range(4)]
for t in readers + churners: t.start()
for t in readers + churners: t.join()

TSAN Report:

WARNING: ThreadSanitizer: data race (pid=94157)
  Read of size 8 at 0x0001081180b0 by thread T1:
    #0 _PyThread_CurrentExceptions pystate.c:2817
    #1 sys__current_exceptions sysmodule.c.h:1180
    #2 cfunction_vectorcall_NOARGS methodobject.c:508
    #3 PyObject_Vectorcall call.c:327
    #4 _Py_VectorCallInstrumentation_StackRefSteal ceval.c:768
    #5 _PyEval_EvalFrameDefault generated_cases.c.h:1906
...
  Previous write of size 8 by thread T9:
    #0 bind_tstate pystate.c:178
    #1 _PyThreadState_Bind pystate.c:2660
    #2 thread_run _threadmodule.c:384
    #3 pythread_wrapper thread_pthread.h:234

SUMMARY: ThreadSanitizer: data race pystate.c:2817 in _PyThread_CurrentExceptions
CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Linked PRs
  • gh-152755
  • gh-156202

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 par bind_tstate() et _PyThread_CurrentExceptions dans Python/pystate.c, puis examinez les PR liés gh-152755 et gh-156202. Exécutez le reproducteur free-threaded fourni avec ThreadSanitizer et vérifiez que la condition de concurrence signalée entre l’écriture et la lecture de thread_id est résolue sans introduire de nouveaux échecs.

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

Évaluation

Stack technique
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
25/100

Recevez les nouvelles issues par e-mail

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