Naserume · GitHub

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

Linked PRs

Read the original on github.com ↗