From 78e40c86a1ae8da39766cfe25423ccc753183750 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Wed, 12 Feb 2025 15:23:55 +0000 Subject: [PATCH 1/2] gh-130019: Fix data race in _PyType_AllocNoTrack The reference count fields, such as `ob_tid` and `ob_ref_shared`, may be accessed concurrently in the free threading build by a `_Py_TryXGetRef` or similar operation. The PyObject header fields will be initialized by `_PyObject_Init`, so only call `memset()` to zero-initialize the remainder of the allocation. --- Objects/typeobject.c | 4 +++- Python/gc.c | 5 +++-- Python/gc_free_threading.c | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 1484d9b33417b2..59270a8e39ed8d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2251,7 +2251,9 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems) if (PyType_IS_GC(type)) { _PyObject_GC_Link(obj); } - memset(obj, '\0', size); + // Zero out the object after the PyObject header. The header fields are + // initialized by _PyObject_Init[Var](). + memset((char *)obj + sizeof(PyObject), '\0', size - sizeof(PyObject)); if (type->tp_itemsize == 0) { _PyObject_Init(obj, type); diff --git a/Python/gc.c b/Python/gc.c index 0fb2f03b0406ad..ab95a192e67a7a 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -2309,11 +2309,12 @@ PyObject * PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *tp, size_t extra_size) { size_t presize = _PyType_PreHeaderSize(tp); - PyObject *op = gc_alloc(tp, _PyObject_SIZE(tp) + extra_size, presize); + size_t size = _PyObject_SIZE(tp) + extra_size; + PyObject *op = gc_alloc(tp, size, presize); if (op == NULL) { return NULL; } - memset(op, 0, _PyObject_SIZE(tp) + extra_size); + memset((char *)op + sizeof(PyObject), 0, size - sizeof(PyObject)); _PyObject_Init(op, tp); return op; } diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 9e459da3a44370..1c7ff5b6650266 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -2593,11 +2593,12 @@ PyObject * PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *tp, size_t extra_size) { size_t presize = _PyType_PreHeaderSize(tp); - PyObject *op = gc_alloc(tp, _PyObject_SIZE(tp) + extra_size, presize); + size_t size = _PyObject_SIZE(tp) + extra_size; + PyObject *op = gc_alloc(tp, size, presize); if (op == NULL) { return NULL; } - memset(op, 0, _PyObject_SIZE(tp) + extra_size); + memset((char *)op + sizeof(PyObject), 0, size - sizeof(PyObject)); _PyObject_Init(op, tp); return op; } From 713177412910aff9e3ecb4e8c94795f7f5444481 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Wed, 12 Feb 2025 21:17:16 -0500 Subject: [PATCH 2/2] Update Objects/typeobject.c Co-authored-by: mpage --- Objects/typeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 59270a8e39ed8d..818a00708b5d3d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2253,7 +2253,7 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems) } // Zero out the object after the PyObject header. The header fields are // initialized by _PyObject_Init[Var](). - memset((char *)obj + sizeof(PyObject), '\0', size - sizeof(PyObject)); + memset((char *)obj + sizeof(PyObject), 0, size - sizeof(PyObject)); if (type->tp_itemsize == 0) { _PyObject_Init(obj, type);