python / python/cpython

Data race reading `UnicodeDecodeError.start`/`end` in `__str__` under free-threading

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

Personne n'a encore pris cette issue.

interpreter-core 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:

On a free-threaded build, UnicodeDecodeError.__str__ reads the start and end members with plain (non-atomic) loads, while assigning to exc.start / exc.end goes through PyMember_SetOne, which does an atomic store. So calling str(exc) on a shared UnicodeDecodeError concurrently with setting its start/end is a data race on those Py_ssize_t members.

start and end are exposed as Py_T_PYSSIZET members:
https://github.com/python/cpython/blob/22a6c51c94a4fde986b8964f1d36d5ec3ac20dcc/Objects/exceptions.c#L3758-L3761

Plain read in UnicodeDecodeError_str:
https://github.com/python/cpython/blob/22a6c51c94a4fde986b8964f1d36d5ec3ac20dcc/Objects/exceptions.c#L3947-L3966

Reproducer:

from threading import Thread

shared_exc = UnicodeDecodeError('utf-8', b'\xff\xfe\xfa', 1, 2, 'invalid start byte')

def chain1_thread():
    for i in range(20000):
        try:
            shared_exc.start = i % 3
        except Exception:
            pass

def chain2_thread():
    for _ in range(20000):
        try:
            str(shared_exc)
        except Exception:
            pass

N_C1 = 4
N_C2 = 8
threads  = [Thread(target=chain1_thread) for _ in range(N_C1)]
threads += [Thread(target=chain2_thread) for _ in range(N_C2)]
for t in threads: t.start()
for t in threads: t.join()

TSAN Report:

WARNING: ThreadSanitizer: data race (pid=656224)
  Read of size 8 at 0x7fffb63734e8 by thread T5:
    #0 UnicodeDecodeError_str /cpython/Objects/exceptions.c:3948:29 
    #1 PyObject_Str /cpython/Objects/object.c:826:11 
    #2 unicode_vectorcall /cpython/Objects/unicodeobject.c:14279:16 
    #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

  Previous atomic write of size 8 at 0x7fffb63734e8 by thread T3:
    #0 PyMember_SetOne /cpython/Python/structmember.c 
    #1 member_set /cpython/Objects/descrobject.c:239:12 
    #2 _PyObject_GenericSetAttrWithDict /cpython/Objects/object.c:2049:19 
    #3 PyObject_GenericSetAttr /cpython/Objects/object.c:2120:12 
    #4 PyObject_SetAttr /cpython/Objects/object.c:1533:15 
    #5 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:12057:27

SUMMARY: ThreadSanitizer: data race/cpython/Objects/exceptions.c:3948:29 in UnicodeDecodeError_str
CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-155255

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 Objects/exceptions.c, au niveau des déclarations de membres Py_T_PYSSIZET et de UnicodeDecodeError_str, puis exécutez le reproducer de threading fourni sur un build free-threaded et examinez le rapport TSAN. Le travail est terminé lorsque la race signalée est corrigée et que les tests CPython pertinents passent.

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é
3/5
Temps estimé
1-2 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.