Skip to content

Commit d73205d

Browse files
bpo-38525: Fix a segmentation fault when using reverse iterators of empty dict (GH-16846)
The reverse iterator for empty dictionaries was not handling correctly shared-key dictionaries. (cherry picked from commit 24dc2f8) Co-authored-by: Dong-hee Na <[email protected]>
1 parent bbd600a commit d73205d

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

Lib/test/test_dict.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,31 @@ def test_reversed(self):
13121312
self.assertEqual(list(r), list('dcba'))
13131313
self.assertRaises(StopIteration, next, r)
13141314

1315+
def test_reverse_iterator_for_empty_dict(self):
1316+
# bpo-38525: revered iterator should work properly
1317+
1318+
# empty dict is directly used for reference count test
1319+
self.assertEqual(list(reversed({})), [])
1320+
self.assertEqual(list(reversed({}.items())), [])
1321+
self.assertEqual(list(reversed({}.values())), [])
1322+
self.assertEqual(list(reversed({}.keys())), [])
1323+
1324+
# dict() and {} don't trigger the same code path
1325+
self.assertEqual(list(reversed(dict())), [])
1326+
self.assertEqual(list(reversed(dict().items())), [])
1327+
self.assertEqual(list(reversed(dict().values())), [])
1328+
self.assertEqual(list(reversed(dict().keys())), [])
1329+
1330+
def test_reverse_iterator_for_shared_shared_dicts(self):
1331+
class A:
1332+
def __init__(self, x, y):
1333+
if x: self.x = x
1334+
if y: self.y = y
1335+
1336+
self.assertEqual(list(reversed(A(1, 2).__dict__)), ['y', 'x'])
1337+
self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x'])
1338+
self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y'])
1339+
13151340
def test_dict_copy_order(self):
13161341
# bpo-34320
13171342
od = collections.OrderedDict([('a', 1), ('b', 2)])
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a segmentation fault when using reverse iterators of empty ``dict`` objects.
2+
Patch by Dong-hee Na and Inada Naoki.

Objects/dictobject.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3462,10 +3462,15 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
34623462
di->di_dict = dict;
34633463
di->di_used = dict->ma_used;
34643464
di->len = dict->ma_used;
3465-
if ((itertype == &PyDictRevIterKey_Type ||
3465+
if (itertype == &PyDictRevIterKey_Type ||
34663466
itertype == &PyDictRevIterItem_Type ||
3467-
itertype == &PyDictRevIterValue_Type) && dict->ma_used) {
3467+
itertype == &PyDictRevIterValue_Type) {
3468+
if (dict->ma_values) {
3469+
di->di_pos = dict->ma_used - 1;
3470+
}
3471+
else {
34683472
di->di_pos = dict->ma_keys->dk_nentries - 1;
3473+
}
34693474
}
34703475
else {
34713476
di->di_pos = 0;

0 commit comments

Comments
 (0)