]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-143100: Fix memcpy data race in setobject.c (gh-143127)
authorSam Gross <colesbury@gmail.com>
Wed, 24 Dec 2025 13:02:19 +0000 (08:02 -0500)
committerGitHub <noreply@github.com>
Wed, 24 Dec 2025 13:02:19 +0000 (08:02 -0500)
Objects/setobject.c
Tools/tsan/suppressions_free_threading.txt

index 5f868c858273eeccd8dd978f31cf19bac8c14349..55e30fe2cdd8f76cfa1b7a3a8ca409c476453a6e 100644 (file)
@@ -1420,6 +1420,17 @@ set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     return make_new_set(type, NULL);
 }
 
+#ifdef Py_GIL_DISABLED
+static void
+copy_small_table(setentry *dest, setentry *src)
+{
+    for (Py_ssize_t i = 0; i < PySet_MINSIZE; i++) {
+        _Py_atomic_store_ptr_release(&dest[i].key, src[i].key);
+        _Py_atomic_store_ssize_relaxed(&dest[i].hash, src[i].hash);
+    }
+}
+#endif
+
 /* set_swap_bodies() switches the contents of any two sets by moving their
    internal data pointers and, if needed, copying the internal smalltables.
    Semantically equivalent to:
@@ -1462,8 +1473,13 @@ set_swap_bodies(PySetObject *a, PySetObject *b)
 
     if (a_table == a->smalltable || b_table == b->smalltable) {
         memcpy(tab, a->smalltable, sizeof(tab));
+#ifndef Py_GIL_DISABLED
         memcpy(a->smalltable, b->smalltable, sizeof(tab));
         memcpy(b->smalltable, tab, sizeof(tab));
+#else
+        copy_small_table(a->smalltable, b->smalltable);
+        copy_small_table(b->smalltable, tab);
+#endif
     }
 
     if (PyType_IsSubtype(Py_TYPE(a), &PyFrozenSet_Type)  &&
index f05e0ded9865f884ee36f9e9c748d6916a5ce8a7..a3e1e54284f0ae8d9c2492bcdff32e608f1f64ee 100644 (file)
@@ -20,6 +20,3 @@ thread:pthread_create
 # PyObject_Realloc internally does memcpy which isn't atomic so can race
 # with non-locking reads. See #132070
 race:PyObject_Realloc
-
-# gh-143100: set_swap_bodies in setobject.c calls memcpy, which isn't atomic
-race:set_swap_bodies