Skip to content

Data race in _PyType_AllocNoTrack in free threaded build #130019

@colesbury

Description

@colesbury

Bug report

Seen in https://github.com/python/cpython/actions/runs/13272883082/job/37056243083?pr=130015

WARNING: ThreadSanitizer: data race (pid=17248)
  Write of size 8 at 0x7fbca00601d0 by thread T2652:
    #0 __tsan_memset <null> (python+0xdc7a8) (BuildId: cba02c45a3623f17982fb0d328c59833[13](https://github.com/python/cpython/actions/runs/13272883082/job/37056243083?pr=130015#step:14:14)92b589)
    #1 _PyType_AllocNoTrack /home/runner/work/cpython/cpython/Objects/typeobject.c:2251:5 (python+0x37dd92) (BuildId: cba02c45a3623f17982fb0d328c598331392b589)
    #2 PyType_GenericAlloc /home/runner/work/cpython/cpython/Objects/typeobject.c:2268:21 (python+0x37daf0) (BuildId: cba02c45a3623f17982fb0d328c598331392b589)
    #3 PyType_GenericNew /home/runner/work/cpython/cpython/Objects/typeobject.c:2282:12 (python+0x37e1a8) (BuildId: cba02c45a3623f17982fb0d328c598331392b589)
    #4 type_call /home/runner/work/cpython/cpython/Objects/typeobject.c:2183:11 (python+0x386be7) (BuildId: cba02c45a3623f17982fb0d328c598331392b589)
    #5 _PyObject_MakeTpCall /home/runner/work/cpython/cpython/Objects/call.c:242:18 (python+0x24ee43) (BuildId: cba02c45a3623f17982fb0d328c598331392b589)
    #6 _PyObject_VectorcallTstate /home/runner/work/cpython/cpython/./Include/internal/pycore_call.h:[16](https://github.com/python/cpython/actions/runs/13272883082/job/37056243083?pr=130015#step:14:17)5:16 (python+0x24e70b) (BuildId: cba02c45a3623f17982fb0d328c598331392b589)
...

Previous atomic read of size 8 at 0x7fbca00601d0 by thread T2651:
    #0 _Py_atomic_load_uintptr_relaxed /home/runner/work/cpython/cpython/./Include/cpython/pyatomic_gcc.h:375:10 (python+0x382bea) (BuildId: cba02c45a3623f17982fb0d328c598331392b589)
    #1 _Py_IsOwnedByCurrentThread /home/runner/work/cpython/cpython/./Include/object.h:252:12 (python+0x382bea)
    #2 _Py_TryIncrefFast /home/runner/work/cpython/cpython/./Include/internal/pycore_object.h:560:9 (python+0x382bea)
    #3 _Py_TryIncref /home/runner/work/cpython/cpython/./Include/internal/pycore_object.h:764:12 (python+0x382bea)
    #4 _PyType_LookupRefAndVersion /home/runner/work/cpython/cpython/Objects/typeobject.c:5567:34 (python+0x382bea)
    #5 _PyType_LookupRef /home/runner/work/cpython/cpython/Objects/typeobject.c:5659:12 (python+0x37e522) (BuildId: cba02c[45](https://github.com/python/cpython/actions/runs/13272883082/job/37056243083?pr=130015#step:14:46)a3623f17982fb0d328c598331392b589)
    #6 _PyObject_GenericSetAttrWithDict /home/runner/work/cpython/cpython/Objects/object.c:1805:13 (python+0x3257d9) (BuildId: cba02c45a3623f17982fb0d328c598331392b589)
...

_PyType_AllocNoTrack() zeroes out the the allocation, including reference count fields. The memset is not atomic and so can race with _Py_TryIncref or similar function.

I think we should memset() on the data after the PyObject header. The ob_type and reference count fields are immediately initialized after the memset anyways by _PyObject_Init or _PyObject_InitVar.

cpython/Objects/typeobject.c

Lines 2248 to 2262 in ed816f1

if (PyType_IS_GC(type)) {
_PyObject_GC_Link(obj);
}
memset(obj, '\0', size);
if (type->tp_itemsize == 0) {
_PyObject_Init(obj, type);
}
else {
_PyObject_InitVar((PyVarObject *)obj, type, nitems);
}
if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
_PyObject_InitInlineValues(obj, type);
}
return obj;

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions