From 7264d66e59cde812e7958fb348e6b7b77d382d2c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: 2026年1月13日 22:29:47 +0200 Subject: [PATCH] gh-141805: Fix crash after concurrent addition objects with the same hash to set This happens when the set contained several elements with the same hash, and then some of them were removed. --- Lib/test/test_set.py | 28 +++++++++++++++++++ ...-01-13-22-26-49.gh-issue-141805.QzIKPS.rst | 3 ++ Objects/setobject.c | 3 ++ 3 files changed, 34 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-01-13-22-26-49.gh-issue-141805.QzIKPS.rst 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 によって変換されたページ (->オリジナル) /