Skip to content

Commit

Permalink
bpo-38588: Fix possible crashes in dict and list when calling PyObjec…
Browse files Browse the repository at this point in the history
…t_RichCompareBool (pythonGH-17734)

Take strong references before calling PyObject_RichCompareBool to protect against the case
where the object dies during the call.
  • Loading branch information
corona10 authored and pablogsal committed Dec 31, 2019
1 parent ee9ff05 commit 2d5bf56
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 1 deletion.
12 changes: 11 additions & 1 deletion Lib/test/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,7 @@ def test_free_after_iterating(self):
support.check_free_after_iterating(self, lambda d: iter(d.items()), dict)

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():
def __del__(self):
dict_b.clear()
Expand All @@ -1237,6 +1237,16 @@ def __hash__(self):
dict_b = {X(): X()}
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):
# test fix for seg fault reported in issue 27945 part 4a.
class X(int):
Expand Down
25 changes: 25 additions & 0 deletions Lib/test/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,31 @@ class L(list): pass
with self.assertRaises(TypeError):
(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)

@cpython_only
def test_preallocation(self):
iterable = [0] * 10
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix possible crashes in dict and list when calling
:c:func:`PyObject_RichCompareBool`.
2 changes: 2 additions & 0 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2777,9 +2777,11 @@ dict_equal(PyDictObject *a, PyDictObject *b)
return -1;
return 0;
}
Py_INCREF(bval);
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
Py_DECREF(key);
Py_DECREF(aval);
Py_DECREF(bval);
if (cmp <= 0) /* error or not equal */
return cmp;
}
Expand Down
7 changes: 7 additions & 0 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2662,8 +2662,15 @@ list_richcompare(PyObject *v, PyObject *w, int op)

/* Search for the first index where items are different */
for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) {
PyObject *vitem = vl->ob_item[i];
PyObject *witem = wl->ob_item[i];

Py_INCREF(vitem);
Py_INCREF(witem);
int k = PyObject_RichCompareBool(vl->ob_item[i],
wl->ob_item[i], Py_EQ);
Py_DECREF(vitem);
Py_DECREF(witem);
if (k < 0)
return NULL;
if (!k)
Expand Down

0 comments on commit 2d5bf56

Please sign in to comment.