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);

AltStyle によって変換されたページ (->オリジナル) /