summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDong-hee Na <donghee.na92@gmail.com>2019-10-19 20:01:08 (GMT)
committerPablo Galindo <Pablogsal@gmail.com>2019-10-19 20:01:08 (GMT)
commit24dc2f8c56697f9ee51a4887cf0814b6600c1815 (patch)
treef5f820dcf20dca38f75b20ddb2ece7a2eace8e4d
parent88eeda6311a8e3bf57136da5f73c70bc91ad79f3 (diff)
downloadcpython-24dc2f8c56697f9ee51a4887cf0814b6600c1815.zip
cpython-24dc2f8c56697f9ee51a4887cf0814b6600c1815.tar.gz
cpython-24dc2f8c56697f9ee51a4887cf0814b6600c1815.tar.bz2
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.
-rw-r--r--Lib/test/test_dict.py25
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2019-10-20-00-36-18.bpo-38525.Vty1cA.rst2
-rw-r--r--Objects/dictobject.c9
3 files changed, 34 insertions, 2 deletions
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index 13be857..5b51376 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -1312,6 +1312,31 @@ class DictTest(unittest.TestCase):
self.assertEqual(list(r), list('dcba'))
self.assertRaises(StopIteration, next, r)
+ def test_reverse_iterator_for_empty_dict(self):
+ # bpo-38525: revered iterator should work properly
+
+ # empty dict is directly used for reference count test
+ self.assertEqual(list(reversed({})), [])
+ self.assertEqual(list(reversed({}.items())), [])
+ self.assertEqual(list(reversed({}.values())), [])
+ self.assertEqual(list(reversed({}.keys())), [])
+
+ # dict() and {} don't trigger the same code path
+ self.assertEqual(list(reversed(dict())), [])
+ self.assertEqual(list(reversed(dict().items())), [])
+ self.assertEqual(list(reversed(dict().values())), [])
+ self.assertEqual(list(reversed(dict().keys())), [])
+
+ def test_reverse_iterator_for_shared_shared_dicts(self):
+ class A:
+ def __init__(self, x, y):
+ if x: self.x = x
+ if y: self.y = y
+
+ self.assertEqual(list(reversed(A(1, 2).__dict__)), ['y', 'x'])
+ self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x'])
+ self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y'])
+
def test_dict_copy_order(self):
# bpo-34320
od = collections.OrderedDict([('a', 1), ('b', 2)])
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-10-20-00-36-18.bpo-38525.Vty1cA.rst b/Misc/NEWS.d/next/Core and Builtins/2019-10-20-00-36-18.bpo-38525.Vty1cA.rst
new file mode 100644
index 0000000..c74d143
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-10-20-00-36-18.bpo-38525.Vty1cA.rst
@@ -0,0 +1,2 @@
+Fix a segmentation fault when using reverse iterators of empty ``dict`` objects.
+Patch by Dong-hee Na and Inada Naoki.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 64876e0..5ac7bb1 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -3452,10 +3452,15 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
di->di_dict = dict;
di->di_used = dict->ma_used;
di->len = dict->ma_used;
- if ((itertype == &PyDictRevIterKey_Type ||
+ if (itertype == &PyDictRevIterKey_Type ||
itertype == &PyDictRevIterItem_Type ||
- itertype == &PyDictRevIterValue_Type) && dict->ma_used) {
+ itertype == &PyDictRevIterValue_Type) {
+ if (dict->ma_values) {
+ di->di_pos = dict->ma_used - 1;
+ }
+ else {
di->di_pos = dict->ma_keys->dk_nentries - 1;
+ }
}
else {
di->di_pos = 0;