From 344081189a439624160fca18cb96c585418f5c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kr=C3=A1l?= Date: Sat, 25 Oct 2025 12:32:04 +0200 Subject: [PATCH 1/4] Enable TestMethodsMutating Make Dict::lookup check if the dictionary has been modified. This is a workaround for a possible segfault. See bpo-46615 for the original discussion in CPython. --- Lib/test/test_set.py | 1 - vm/src/dict_inner.rs | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 8ef2871c61..279c068277 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1898,7 +1898,6 @@ class TestBinaryOpsMutating_Subclass_Set(TestBinaryOpsMutating, unittest.TestCas constructor2 = set -@unittest.skip("TODO: RUSTPYTHON; segfault") class TestMethodsMutating(TestOperationsMutating): def test_issubset_with_mutation(self): diff --git a/vm/src/dict_inner.rs b/vm/src/dict_inner.rs index 02e237afb0..3aa02f273f 100644 --- a/vm/src/dict_inner.rs +++ b/vm/src/dict_inner.rs @@ -582,10 +582,12 @@ impl Dict { }); loop { let index_index = idxs.next(); - let index_entry = *unsafe { - // Safety: index_index is generated - inner.indices.get_unchecked(index_index) - }; + let index_entry_ptr = inner.indices.get(index_index); + if index_entry_ptr.is_none() { + // Dictionary was modified under our hands, see TestMethodsMutating. + continue 'outer; + } + let index_entry = *index_entry_ptr.unwrap(); match index_entry { IndexEntry::DUMMY => { if free_slot.is_none() { From 35d81206c5c27a84ce02c7813f9ebb3cf1193cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kr=C3=A1l?= Date: Sat, 25 Oct 2025 13:52:45 +0200 Subject: [PATCH 2/4] [squash] Maybe a forgotten skipped test? --- Lib/test/test_set.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 279c068277..f7fc65512c 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1809,7 +1809,6 @@ def check_set_op_does_not_crash(self, function): self.assertIn("changed size during iteration", str(e)) -@unittest.skip("TODO: RUSTPYTHON; segfault") class TestBinaryOpsMutating(TestOperationsMutating): def test_eq_with_mutation(self): From d7c8839b65cc5abc14e2e49faf9f5e8824958f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kr=C3=A1l?= Date: Sun, 26 Oct 2025 10:27:00 +0100 Subject: [PATCH 3/4] [squash] add cleanup by ShaharNaveh --- vm/src/dict_inner.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/vm/src/dict_inner.rs b/vm/src/dict_inner.rs index 3aa02f273f..e1e2e203be 100644 --- a/vm/src/dict_inner.rs +++ b/vm/src/dict_inner.rs @@ -582,12 +582,13 @@ impl Dict { }); loop { let index_index = idxs.next(); - let index_entry_ptr = inner.indices.get(index_index); - if index_entry_ptr.is_none() { - // Dictionary was modified under our hands, see TestMethodsMutating. - continue 'outer; - } - let index_entry = *index_entry_ptr.unwrap(); + let index_entry = match inner.indices.get(index_index) { + None => { + // Dictionary was modified under our hands, see TestMethodsMutating. + continue 'outer; + } + Some(v) => *v, + }; match index_entry { IndexEntry::DUMMY => { if free_slot.is_none() { From 71066cee1c60fc3e06f9a99937ad910abf79a217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kr=C3=A1l?= Date: Tue, 28 Oct 2025 08:19:43 +0100 Subject: [PATCH 4/4] [squash] Test of changed size in the lookup mehtod --- vm/src/dict_inner.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/vm/src/dict_inner.rs b/vm/src/dict_inner.rs index e1e2e203be..f62e31e243 100644 --- a/vm/src/dict_inner.rs +++ b/vm/src/dict_inner.rs @@ -574,6 +574,7 @@ impl Dict { ) -> PyResult { let mut idxs = None; let mut free_slot = None; + let original_size = self.size(); let ret = 'outer: loop { let (entry_key, ret) = { let inner = lock.take().unwrap_or_else(|| self.read()); @@ -585,7 +586,7 @@ impl Dict { let index_entry = match inner.indices.get(index_index) { None => { // Dictionary was modified under our hands, see TestMethodsMutating. - continue 'outer; + return Err(vm.new_runtime_error("set changed size during iteration")); } Some(v) => *v, }; @@ -600,6 +601,11 @@ impl Dict { Some(free) => (IndexEntry::DUMMY, free), None => (IndexEntry::FREE, index_index), }; + if self.has_changed_size(&original_size) { + return Err( + vm.new_runtime_error("set changed size during iteration") + ); + } return Ok(idxs); } idx => { @@ -631,6 +637,9 @@ impl Dict { // warn!("Perturb value: {}", i); }; + if self.has_changed_size(&original_size) { + return Err(vm.new_runtime_error("set changed size during iteration")); + } Ok(ret) }