https://github.com/python/cpython/commit/b8e925b4f8f6c5e28fbebc4f3965bf77610698b3
commit: b8e925b4f8f6c5e28fbebc4f3965bf77610698b3
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-01-14T23:29:17+02:00
summary:
gh-141805: Fix crash after concurrent addition objects with the same hash to
set (GH-143815)
This happens when the set contained several elements with the same hash,
and then some of them were removed.
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 203a231201c669..3539adc789c7e7 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 55e30fe2cdd8f7..378f221bcfd1e1 100644
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -308,6 +308,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);
FT_ATOMIC_STORE_SSIZE_RELAXED(freeslot->hash, hash);
FT_ATOMIC_STORE_PTR_RELEASE(freeslot->key, key);
_______________________________________________
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]