Skip to content

Commit 9133d5c

Browse files
gh-152107: Fix crash when creating a dict item iterator under MemoryError (#152110)
Co-authored-by: Sergey Miryanov <sergey.miryanov@gmail.com>
1 parent 84b0669 commit 9133d5c

3 files changed

Lines changed: 28 additions & 4 deletions

File tree

‎Lib/test/test_dict.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,26 @@ def test_items(self):
116116
self.assertRaises(TypeError, d.items, None)
117117
self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])")
118118

119+
@support.cpython_only
120+
def test_item_iterator_oom(self):
121+
import_helper.import_module('_testcapi')
122+
from test.support.script_helper import assert_python_ok
123+
code = """if 1:
124+
import _testcapi
125+
items = {1: 2, 3: 4}.items()
126+
ballast = [(i, i) for i in range(3000)]
127+
held = []
128+
for start in range(1, 5):
129+
_testcapi.set_nomemory(start)
130+
try:
131+
held.append(iter(items))
132+
except MemoryError:
133+
pass
134+
finally:
135+
_testcapi.remove_mem_hooks()
136+
"""
137+
assert_python_ok('-c', code)
138+
119139
def test_views_mapping(self):
120140
mappingproxy = type(type.__dict__)
121141
class Dict(dict):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash when creating a :class:`dict` item iterator (for example
2+
``iter(d.items())`` or ``reversed(d.items())``) under a memory-allocation
3+
failure. Patch by Jiucheng Zang.

‎Objects/dictobject.c‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5632,6 +5632,7 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
56325632
used = GET_USED(dict);
56335633
di->di_used = used;
56345634
di->len = used;
5635+
di->di_result = NULL;
56355636
if (itertype == &PyDictRevIterKey_Type ||
56365637
itertype == &PyDictRevIterItem_Type ||
56375638
itertype == &PyDictRevIterValue_Type) {
@@ -5645,6 +5646,10 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
56455646
else {
56465647
di->di_pos = 0;
56475648
}
5649+
/* gh-152107: track before allocating di_result. A dictiter with a NULL
5650+
di_result is a valid state for dictiter_traverse()/dictiter_dealloc(),
5651+
so a failure of the allocation below can safely DECREF a tracked di. */
5652+
_PyObject_GC_TRACK(di);
56485653
if (itertype == &PyDictIterItem_Type ||
56495654
itertype == &PyDictRevIterItem_Type) {
56505655
di->di_result = _PyTuple_FromPairSteal(Py_None, Py_None);
@@ -5653,10 +5658,6 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
56535658
return NULL;
56545659
}
56555660
}
5656-
else {
5657-
di->di_result = NULL;
5658-
}
5659-
_PyObject_GC_TRACK(di);
56605661
return (PyObject *)di;
56615662
}
56625663

0 commit comments

Comments
 (0)