Skip to content

Commit 2d5bf56

Browse files
corona10pablogsal
authored andcommitted
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.
1 parent ee9ff05 commit 2d5bf56

File tree

5 files changed

+47
-1
lines changed

5 files changed

+47
-1
lines changed

Lib/test/test_dict.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,7 @@ def test_free_after_iterating(self):
12211221
support.check_free_after_iterating(self, lambda d: iter(d.items()), dict)
12221222

12231223
def test_equal_operator_modifying_operand(self):
1224-
# test fix for seg fault reported in issue 27945 part 3.
1224+
# test fix for seg fault reported in bpo-27945 part 3.
12251225
class X():
12261226
def __del__(self):
12271227
dict_b.clear()
@@ -1237,6 +1237,16 @@ def __hash__(self):
12371237
dict_b = {X(): X()}
12381238
self.assertTrue(dict_a == dict_b)
12391239

1240+
# test fix for seg fault reported in bpo-38588 part 1.
1241+
class Y:
1242+
def __eq__(self, other):
1243+
dict_d.clear()
1244+
return True
1245+
1246+
dict_c = {0: Y()}
1247+
dict_d = {0: set()}
1248+
self.assertTrue(dict_c == dict_d)
1249+
12401250
def test_fromkeys_operator_modifying_dict_operand(self):
12411251
# test fix for seg fault reported in issue 27945 part 4a.
12421252
class X(int):

Lib/test/test_list.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,31 @@ class L(list): pass
163163
with self.assertRaises(TypeError):
164164
(3,) + L([1,2])
165165

166+
def test_equal_operator_modifying_operand(self):
167+
# test fix for seg fault reported in bpo-38588 part 2.
168+
class X:
169+
def __eq__(self,other) :
170+
list2.clear()
171+
return NotImplemented
172+
173+
class Y:
174+
def __eq__(self, other):
175+
list1.clear()
176+
return NotImplemented
177+
178+
class Z:
179+
def __eq__(self, other):
180+
list3.clear()
181+
return NotImplemented
182+
183+
list1 = [X()]
184+
list2 = [Y()]
185+
self.assertTrue(list1 == list2)
186+
187+
list3 = [Z()]
188+
list4 = [1]
189+
self.assertFalse(list3 == list4)
190+
166191
@cpython_only
167192
def test_preallocation(self):
168193
iterable = [0] * 10
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix possible crashes in dict and list when calling
2+
:c:func:`PyObject_RichCompareBool`.

Objects/dictobject.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2777,9 +2777,11 @@ dict_equal(PyDictObject *a, PyDictObject *b)
27772777
return -1;
27782778
return 0;
27792779
}
2780+
Py_INCREF(bval);
27802781
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
27812782
Py_DECREF(key);
27822783
Py_DECREF(aval);
2784+
Py_DECREF(bval);
27832785
if (cmp <= 0) /* error or not equal */
27842786
return cmp;
27852787
}

Objects/listobject.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,8 +2662,15 @@ list_richcompare(PyObject *v, PyObject *w, int op)
26622662

26632663
/* Search for the first index where items are different */
26642664
for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) {
2665+
PyObject *vitem = vl->ob_item[i];
2666+
PyObject *witem = wl->ob_item[i];
2667+
2668+
Py_INCREF(vitem);
2669+
Py_INCREF(witem);
26652670
int k = PyObject_RichCompareBool(vl->ob_item[i],
26662671
wl->ob_item[i], Py_EQ);
2672+
Py_DECREF(vitem);
2673+
Py_DECREF(witem);
26672674
if (k < 0)
26682675
return NULL;
26692676
if (!k)

0 commit comments

Comments
 (0)