[3.7] bpo-38588: Fix possible crashes in dict and list when calling P… (GH-17765)

* [3.7] bpo-38588: Fix possible crashes in dict and list when calling PyObject_RichCompareBool (GH-17734)

Take strong references before calling PyObject_RichCompareBool to protect against the case
where the object dies during the call..
(cherry picked from commit 2d5bf568ea)

Co-authored-by: Dong-hee Na <donghee.na92@gmail.com>

* methane's suggestion

methane's suggestion

Co-Authored-By: Inada Naoki <songofacandy@gmail.com>

Co-authored-by: Inada Naoki <songofacandy@gmail.com>
This commit is contained in:
Dong-hee Na 2019-12-31 13:15:10 +09:00 committed by Pablo Galindo
parent c9c17cc933
commit 53f11ba7b1
5 changed files with 50 additions and 1 deletions

View File

@ -1138,7 +1138,7 @@ class DictTest(unittest.TestCase):
support.check_free_after_iterating(self, lambda d: iter(d.items()), dict) support.check_free_after_iterating(self, lambda d: iter(d.items()), dict)
def test_equal_operator_modifying_operand(self): def test_equal_operator_modifying_operand(self):
# test fix for seg fault reported in issue 27945 part 3. # test fix for seg fault reported in bpo-27945 part 3.
class X(): class X():
def __del__(self): def __del__(self):
dict_b.clear() dict_b.clear()
@ -1154,6 +1154,16 @@ class DictTest(unittest.TestCase):
dict_b = {X(): X()} dict_b = {X(): X()}
self.assertTrue(dict_a == dict_b) self.assertTrue(dict_a == dict_b)
# test fix for seg fault reported in bpo-38588 part 1.
class Y:
def __eq__(self, other):
dict_d.clear()
return True
dict_c = {0: Y()}
dict_d = {0: set()}
self.assertTrue(dict_c == dict_d)
def test_fromkeys_operator_modifying_dict_operand(self): def test_fromkeys_operator_modifying_dict_operand(self):
# test fix for seg fault reported in issue 27945 part 4a. # test fix for seg fault reported in issue 27945 part 4a.
class X(int): class X(int):

View File

@ -162,6 +162,31 @@ class ListTest(list_tests.CommonTest):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
(3,) + L([1,2]) (3,) + L([1,2])
def test_equal_operator_modifying_operand(self):
# test fix for seg fault reported in bpo-38588 part 2.
class X:
def __eq__(self,other) :
list2.clear()
return NotImplemented
class Y:
def __eq__(self, other):
list1.clear()
return NotImplemented
class Z:
def __eq__(self, other):
list3.clear()
return NotImplemented
list1 = [X()]
list2 = [Y()]
self.assertTrue(list1 == list2)
list3 = [Z()]
list4 = [1]
self.assertFalse(list3 == list4)
def test_count_index_remove_crashes(self): def test_count_index_remove_crashes(self):
# bpo-38610: The count(), index(), and remove() methods were not # bpo-38610: The count(), index(), and remove() methods were not
# holding strong references to list elements while calling # holding strong references to list elements while calling

View File

@ -0,0 +1,2 @@
Fix possible crashes in dict and list when calling
:c:func:`PyObject_RichCompareBool`.

View File

@ -2677,9 +2677,11 @@ dict_equal(PyDictObject *a, PyDictObject *b)
return -1; return -1;
return 0; return 0;
} }
Py_INCREF(bval);
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
Py_DECREF(key); Py_DECREF(key);
Py_DECREF(aval); Py_DECREF(aval);
Py_DECREF(bval);
if (cmp <= 0) /* error or not equal */ if (cmp <= 0) /* error or not equal */
return cmp; return cmp;
} }

View File

@ -2615,8 +2615,18 @@ list_richcompare(PyObject *v, PyObject *w, int op)
/* Search for the first index where items are different */ /* Search for the first index where items are different */
for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) { for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) {
PyObject *vitem = vl->ob_item[i];
PyObject *witem = wl->ob_item[i];
if (vitem == witem) {
continue;
}
Py_INCREF(vitem);
Py_INCREF(witem);
int k = PyObject_RichCompareBool(vl->ob_item[i], int k = PyObject_RichCompareBool(vl->ob_item[i],
wl->ob_item[i], Py_EQ); wl->ob_item[i], Py_EQ);
Py_DECREF(vitem);
Py_DECREF(witem);
if (k < 0) if (k < 0)
return NULL; return NULL;
if (!k) if (!k)