diff options
| -rw-r--r-- | Lib/test/test_dict.py | 29 | ||||
| -rw-r--r-- | Misc/NEWS | 3 | ||||
| -rw-r--r-- | Objects/dictobject.c | 6 | 
3 files changed, 35 insertions, 3 deletions
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index a38895945e..98d8a3bd00 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -906,6 +906,35 @@ class DictTest(unittest.TestCase):          f.a = 'a'          self.assertEqual(f.__dict__, {1:1, 'a':'a'}) +    def check_reentrant_insertion(self, mutate): +        # This object will trigger mutation of the dict when replaced +        # by another value.  Note this relies on refcounting: the test +        # won't achieve its purpose on fully-GCed Python implementations. +        class Mutating: +            def __del__(self): +                mutate(d) + +        d = {k: Mutating() for k in 'abcdefghijklmnopqr'} +        for k in list(d): +            d[k] = k + +    def test_reentrant_insertion(self): +        # Reentrant insertion shouldn't crash (see issue #22653) +        def mutate(d): +            d['b'] = 5 +        self.check_reentrant_insertion(mutate) + +        def mutate(d): +            d.update(self.__dict__) +            d.clear() +        self.check_reentrant_insertion(mutate) + +        def mutate(d): +            while d: +                d.popitem() +        self.check_reentrant_insertion(mutate) + +  from test import mapping_tests  class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): @@ -11,6 +11,9 @@ Release date: TBA  Core and Builtins  ----------------- +- Issue #22653: Fix an assertion failure in debug mode when doing a reentrant +  dict insertion in debug mode. +  - Issue #22643: Fix integer overflow in Unicode case operations (upper, lower,    title, swapcase, casefold). diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 1ccea6ef2a..bab6242631 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -814,13 +814,14 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)      if (ep == NULL) {          return -1;      } +    assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);      Py_INCREF(value);      MAINTAIN_TRACKING(mp, key, value);      old_value = *value_addr;      if (old_value != NULL) {          assert(ep->me_key != NULL && ep->me_key != dummy);          *value_addr = value; -        Py_DECREF(old_value); /* which **CAN** re-enter */ +        Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */      }      else {          if (ep->me_key == NULL) { @@ -851,9 +852,8 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)          }          mp->ma_used++;          *value_addr = value; +        assert(ep->me_key != NULL && ep->me_key != dummy);      } -    assert(ep->me_key != NULL && ep->me_key != dummy); -    assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);      return 0;  }  | 
