https://github.com/python/cpython/commit/601ccb7f89dc4b6b1da9c5322f0c3abd875fcf2b
commit: 601ccb7f89dc4b6b1da9c5322f0c3abd875fcf2b
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-01-15T09:31:16+02:00
summary:

[3.14] gh-141805: Fix crash after concurrent addition objects with the same 
hash to set (GH-143815) (GH-143849)

This happens when the set contained several elements with the same hash,
and then some of them were removed.
(cherry picked from commit b8e925b4f8f6c5e28fbebc4f3965bf77610698b3)

Co-authored-by: Serhiy Storchaka <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-01-13-22-26-49.gh-issue-141805.QzIKPS.rst
M Lib/test/test_set.py
M Objects/setobject.c

diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py
index c0df9507bd7f5e..5f38f8e319efc3 100644
--- a/Lib/test/test_set.py
+++ b/Lib/test/test_set.py
@@ -1853,6 +1853,7 @@ def test_iter_and_mutate(self):
         list(si)
 
     def test_merge_and_mutate(self):
+        # gh-141805
         class X:
             def __hash__(self):
                 return hash(0)
@@ -1865,6 +1866,33 @@ def __eq__(self, o):
         s = {0}
         s.update(other)
 
+    def test_hash_collision_concurrent_add(self):
+        class X:
+            def __hash__(self):
+                return 0
+        class Y:
+            flag = False
+            def __hash__(self):
+                return 0
+            def __eq__(self, other):
+                if not self.flag:
+                    self.flag = True
+                    s.add(X())
+                return self is other
+
+        a = X()
+        s = set()
+        s.add(a)
+        s.add(X())
+        s.remove(a)
+        # Now the set contains a dummy entry followed by an entry
+        # for an object with hash 0.
+        s.add(Y())
+        # The following operations should not crash.
+        repr(s)
+        list(s)
+        set() | s
+
 
 class TestOperationsMutating:
     """Regression test for bpo-46615"""
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-01-13-22-26-49.gh-issue-141805.QzIKPS.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-13-22-26-49.gh-issue-141805.QzIKPS.rst
new file mode 100644
index 00000000000000..8878d872c5b3a7
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-13-22-26-49.gh-issue-141805.QzIKPS.rst
@@ -0,0 +1,3 @@
+Fix crash in :class:`set` when objects with the same hash are concurrently
+added to the set after removing an element with the same hash while the set
+still contains elements with the same hash.
diff --git a/Objects/setobject.c b/Objects/setobject.c
index ff4844b24371e8..304d8b7a55e182 100644
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -190,6 +190,9 @@ set_add_entry_takeref(PySetObject *so, PyObject *key, 
Py_hash_t hash)
   found_unused_or_dummy:
     if (freeslot == NULL)
         goto found_unused;
+    if (freeslot->hash != -1) {
+        goto restart;
+    }
     FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
     freeslot->key = key;
     freeslot->hash = hash;

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to