https://github.com/python/cpython/commit/9133d5c2cb4ace240306f151b54c52a47136af81
commit: 9133d5c2cb4ace240306f151b54c52a47136af81
branch: main
author: Jiucheng(Oliver) <[email protected]>
committer: methane <[email protected]>
date: 2026-10-05T18:10:14+09:00
summary:

gh-152107: Fix crash when creating a dict item iterator under MemoryError 
(#152110)

Co-authored-by: Sergey Miryanov <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-06-24-14-05-00.gh-issue-152107.K3jX9p.rst
M Lib/test/test_dict.py
M Objects/dictobject.c

diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index 3b9ff672a1aa03..26bdaa9d6a35b7 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -116,6 +116,26 @@ def test_items(self):
         self.assertRaises(TypeError, d.items, None)
         self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])")
 
+    @support.cpython_only
+    def test_item_iterator_oom(self):
+        import_helper.import_module('_testcapi')
+        from test.support.script_helper import assert_python_ok
+        code = """if 1:
+            import _testcapi
+            items = {1: 2, 3: 4}.items()
+            ballast = [(i, i) for i in range(3000)]
+            held = []
+            for start in range(1, 5):
+                _testcapi.set_nomemory(start)
+                try:
+                    held.append(iter(items))
+                except MemoryError:
+                    pass
+                finally:
+                    _testcapi.remove_mem_hooks()
+            """
+        assert_python_ok('-c', code)
+
     def test_views_mapping(self):
         mappingproxy = type(type.__dict__)
         class Dict(dict):
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-24-14-05-00.gh-issue-152107.K3jX9p.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-24-14-05-00.gh-issue-152107.K3jX9p.rst
new file mode 100644
index 00000000000000..a8f5a99ee06524
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-24-14-05-00.gh-issue-152107.K3jX9p.rst
@@ -0,0 +1,3 @@
+Fix a crash when creating a :class:`dict` item iterator (for example
+``iter(d.items())`` or ``reversed(d.items())``) under a memory-allocation
+failure.  Patch by Jiucheng Zang.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index c6d055a412a515..bab983c2509c2c 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -5632,6 +5632,7 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
     used = GET_USED(dict);
     di->di_used = used;
     di->len = used;
+    di->di_result = NULL;
     if (itertype == &PyDictRevIterKey_Type ||
          itertype == &PyDictRevIterItem_Type ||
          itertype == &PyDictRevIterValue_Type) {
@@ -5645,6 +5646,10 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
     else {
         di->di_pos = 0;
     }
+    /* gh-152107: track before allocating di_result. A dictiter with a NULL
+       di_result is a valid state for dictiter_traverse()/dictiter_dealloc(),
+       so a failure of the allocation below can safely DECREF a tracked di. */
+    _PyObject_GC_TRACK(di);
     if (itertype == &PyDictIterItem_Type ||
         itertype == &PyDictRevIterItem_Type) {
         di->di_result = _PyTuple_FromPairSteal(Py_None, Py_None);
@@ -5653,10 +5658,6 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
             return NULL;
         }
     }
-    else {
-        di->di_result = NULL;
-    }
-    _PyObject_GC_TRACK(di);
     return (PyObject *)di;
 }
 

_______________________________________________
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]

Reply via email to