Skip to content

Data race between deque.index and deque mutators on Py_SIZE with free-threading #150750

@Naserume

Description

@Naserume

Bug report

Bug description:

deque_index's clinic input declares stop's default as Py_SIZE(deque),

@text_signature "($self, value, [start, [stop]])"
_collections.deque.index as deque_index
deque: dequeobject
value as v: object
start: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='0') = NULL
stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='Py_SIZE(deque)') = NULL
/

and the generated wrapper reads Py_SIZE(deque) non-atomically before entering the critical_section.

static PyObject *
deque_index(PyObject *deque, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
PyObject *v;
Py_ssize_t start = 0;
Py_ssize_t stop = Py_SIZE(deque);

Meanwhile mutators (deque_clear, deque_append_lock_held, deque_appendleft_lock_held) update the same field via Py_SET_SIZE.

Py_SET_SIZE(deque, Py_SIZE(deque) + 1);

Py_SET_SIZE(deque, Py_SIZE(deque) + 1);

Py_SET_SIZE(deque, 0);

Reproducer:

import collections
from threading import Thread, Barrier

d = collections.deque(range(100))

N_MUT = 3
ITERS = 10000
barrier = Barrier(1 + N_MUT)

def index_thread():
    barrier.wait()
    for _ in range(ITERS):
        try:
            d.index(50)
        except ValueError:
            pass

def mut_thread():
    barrier.wait()
    for _ in range(ITERS):
        d.append(0)
        d.clear()
        d.extend(range(100))
        d.appendleft(-1)

if __name__ == "__main__":
    threads = [Thread(target=index_thread)]
    threads += [Thread(target=mut_thread) for _ in range(N_MUT)]
    for t in threads: t.start()
    for t in threads: t.join()

TSAN Report:

WARNING: ThreadSanitizer: data race (pid=745816)
  Read of size 8 at 0x7fffb63e9f20 by thread T1:
    #0 deque_index /cpython/./Modules/clinic/_collectionsmodule.c.h:343:23 (python3.16t+0x5bd290) 
    #1 method_vectorcall_FASTCALL /cpython/Objects/descrobject.c:402:24 (python3.16t+0x22e771) 
    #2 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x2149a8) 
    #3 PyObject_Vectorcall /cpython/Objects/call.c:327:12 (python3.16t+0x2149a8)
    #4 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:766:11 (python3.16t+0x4370c8) 
    #5 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1846:35 (python3.16t+0x43d022) 
    #6 _PyEval_EvalFrame /cpython/./Include/internal/pycore_ceval.h:122:16 (python3.16t+0x436463) 
    #7 _PyEval_Vector /cpython/Python/ceval.c:2134:12 (python3.16t+0x436463)
    #8 _PyFunction_Vectorcall /cpython/Objects/call.c (python3.16t+0x215013) 
    #9 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x216b11) 
    #10 _PyObject_VectorcallPrepend /cpython/Objects/call.c:855:20 (python3.16t+0x216b11)
    #11 method_vectorcall /cpython/Objects/classobject.c:55:12 (python3.16t+0x21a132) 
    #12 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x48ec15) 
    #13 context_run /cpython/Python/context.c:728:29 (python3.16t+0x48ec15)
    #14 method_vectorcall_FASTCALL_KEYWORDS /cpython/Objects/descrobject.c:421:24 (python3.16t+0x22e84c) 
    #15 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x2149a8) 
    #16 PyObject_Vectorcall /cpython/Objects/call.c:327:12 (python3.16t+0x2149a8)
    #17 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:766:11 (python3.16t+0x4370c8) 
    #18 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1846:35 (python3.16t+0x43d022) 
    #19 _PyEval_EvalFrame /cpython/./Include/internal/pycore_ceval.h:122:16 (python3.16t+0x436463) 
    #20 _PyEval_Vector /cpython/Python/ceval.c:2134:12 (python3.16t+0x436463)
    #21 _PyFunction_Vectorcall /cpython/Objects/call.c (python3.16t+0x215013) 
    #22 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x216b11) 
    #23 _PyObject_VectorcallPrepend /cpython/Objects/call.c:855:20 (python3.16t+0x216b11)
    #24 method_vectorcall /cpython/Objects/classobject.c:55:12 (python3.16t+0x21a132) 
    #25 _PyVectorcall_Call /cpython/Objects/call.c:273:16 (python3.16t+0x214ca2) 
    #26 _PyObject_Call /cpython/Objects/call.c:348:16 (python3.16t+0x214ca2)
    #27 PyObject_Call /cpython/Objects/call.c:373:12 (python3.16t+0x214d07) 
    #28 thread_run /cpython/./Modules/_threadmodule.c:388:21 (python3.16t+0x61a708) 
    #29 pythread_wrapper /cpython/Python/thread_pthread.h:234:5 (python3.16t+0x5473bb) 

  Previous atomic write of size 8 at 0x7fffb63e9f20 by thread T2:
    #0 _Py_atomic_store_ssize_relaxed /cpython/./Include/cpython/pyatomic_gcc.h:513:3 (python3.16t+0x5bc41e) 
    #1 _Py_SET_SIZE_impl /cpython/./Include/object.h:258:5 (python3.16t+0x5bc41e)
    #2 deque_append_lock_held /cpython/./Modules/_collectionsmodule.c:356:5 (python3.16t+0x5bc41e)
    #3 deque_extend_impl /cpython/./Modules/_collectionsmodule.c:514:13 (python3.16t+0x5bbeaf) 
    #4 deque_extend /cpython/./Modules/clinic/_collectionsmodule.c.h:127:20 (python3.16t+0x5bc29b) 
    #5 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:4261:35 (python3.16t+0x443538) 
    #6 _PyEval_EvalFrame /cpython/./Include/internal/pycore_ceval.h:122:16 (python3.16t+0x436463) 
    #7 _PyEval_Vector /cpython/Python/ceval.c:2134:12 (python3.16t+0x436463)
    #8 _PyFunction_Vectorcall /cpython/Objects/call.c (python3.16t+0x215013) 
    #9 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x216b11) 
    #10 _PyObject_VectorcallPrepend /cpython/Objects/call.c:855:20 (python3.16t+0x216b11)
    #11 method_vectorcall /cpython/Objects/classobject.c:55:12 (python3.16t+0x21a132) 
    #12 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x48ec15) 
    #13 context_run /cpython/Python/context.c:728:29 (python3.16t+0x48ec15)
    #14 method_vectorcall_FASTCALL_KEYWORDS /cpython/Objects/descrobject.c:421:24 (python3.16t+0x22e84c) 
    #15 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x2149a8) 
    #16 PyObject_Vectorcall /cpython/Objects/call.c:327:12 (python3.16t+0x2149a8)
    #17 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:766:11 (python3.16t+0x4370c8) 
    #18 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1846:35 (python3.16t+0x43d022) 
    #19 _PyEval_EvalFrame /cpython/./Include/internal/pycore_ceval.h:122:16 (python3.16t+0x436463) 
    #20 _PyEval_Vector /cpython/Python/ceval.c:2134:12 (python3.16t+0x436463)
    #21 _PyFunction_Vectorcall /cpython/Objects/call.c (python3.16t+0x215013) 
    #22 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 (python3.16t+0x216b11) 
    #23 _PyObject_VectorcallPrepend /cpython/Objects/call.c:855:20 (python3.16t+0x216b11)
    #24 method_vectorcall /cpython/Objects/classobject.c:55:12 (python3.16t+0x21a132) 
    #25 _PyVectorcall_Call /cpython/Objects/call.c:273:16 (python3.16t+0x214ca2) 
    #26 _PyObject_Call /cpython/Objects/call.c:348:16 (python3.16t+0x214ca2)
    #27 PyObject_Call /cpython/Objects/call.c:373:12 (python3.16t+0x214d07) 
    #28 thread_run /cpython/./Modules/_threadmodule.c:388:21 (python3.16t+0x61a708) 
    #29 pythread_wrapper /cpython/Python/thread_pthread.h:234:5 (python3.16t+0x5473bb) 

...

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions