Skip to content

Commit c41df03

Browse files
committed
Address Review Comments
1 parent a46db4f commit c41df03

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
@@ -5507,6 +5507,7 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
55075507
used = GET_USED(dict);
55085508
di->di_used = used;
55095509
di->len = used;
5510+
di->di_result = NULL;
55105511
if (itertype == &PyDictRevIterKey_Type ||
55115512
itertype == &PyDictRevIterItem_Type ||
55125513
itertype == &PyDictRevIterValue_Type) {
@@ -5520,6 +5521,10 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
55205521
else {
55215522
di->di_pos = 0;
55225523
}
5524+
/* gh-152107: track before allocating di_result. A dictiter with a NULL
5525+
di_result is a valid state for dictiter_traverse()/dictiter_dealloc(),
5526+
so a failure of the allocation below can safely DECREF a tracked di. */
5527+
_PyObject_GC_TRACK(di);
55235528
if (itertype == &PyDictIterItem_Type ||
55245529
itertype == &PyDictRevIterItem_Type) {
55255530
di->di_result = _PyTuple_FromPairSteal(Py_None, Py_None);
@@ -5528,10 +5533,6 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
55285533
return NULL;
55295534
}
55305535
}
5531-
else {
5532-
di->di_result = NULL;
5533-
}
5534-
_PyObject_GC_TRACK(di);
55355536
return (PyObject *)di;
55365537
}
55375538

0 commit comments

Comments
 (0)