Commit e6a0b598 authored by Serhiy Storchaka's avatar Serhiy Storchaka Committed by GitHub

[2.7] bpo-27945: Fixed various segfaults with dict. (GH-1657) (#1681)

Based on patches by Duane Griffin and Tim Mitchell.
(cherry picked from commit 753bca39)
parent e9f9b042
...@@ -690,6 +690,97 @@ class DictTest(unittest.TestCase): ...@@ -690,6 +690,97 @@ class DictTest(unittest.TestCase):
test_support.check_free_after_iterating(self, lambda d: iter(d.viewvalues()), dict) test_support.check_free_after_iterating(self, lambda d: iter(d.viewvalues()), dict)
test_support.check_free_after_iterating(self, lambda d: iter(d.viewitems()), dict) test_support.check_free_after_iterating(self, lambda d: iter(d.viewitems()), dict)
def test_equal_operator_modifying_operand(self):
# test fix for seg fault reported in issue 27945 part 3.
class X(object):
def __del__(self):
dict_b.clear()
def __eq__(self, other):
dict_a.clear()
return True
def __hash__(self):
return 13
dict_a = {X(): 0}
dict_b = {X(): X()}
self.assertTrue(dict_a == dict_b)
def test_fromkeys_operator_modifying_dict_operand(self):
# test fix for seg fault reported in issue 27945 part 4a.
class X(int):
def __hash__(self):
return 13
def __eq__(self, other):
if len(d) > 1:
d.clear()
return False
d = {} # this is required to exist so that d can be constructed!
d = {X(1): 1, X(2): 2}
try:
dict.fromkeys(d) # shouldn't crash
except RuntimeError: # implementation defined
pass
def test_fromkeys_operator_modifying_set_operand(self):
# test fix for seg fault reported in issue 27945 part 4b.
class X(int):
def __hash__(self):
return 13
def __eq__(self, other):
if len(d) > 1:
d.clear()
return False
d = {} # this is required to exist so that d can be constructed!
d = {X(1), X(2)}
try:
dict.fromkeys(d) # shouldn't crash
except RuntimeError: # implementation defined
pass
def test_dictitems_contains_use_after_free(self):
class X(object):
def __eq__(self, other):
d.clear()
return NotImplemented
__hash__ = object.__hash__ # silence Py3k warning
d = {0: set()}
try:
(0, X()) in d.iteritems() # shouldn't crash
except RuntimeError: # implementation defined
pass
def test_init_use_after_free(self):
class X(object):
def __hash__(self):
pair[:] = []
return 13
pair = [X(), 123]
dict([pair])
def test_oob_indexing_dictiter_iternextitem(self):
class X(int):
def __del__(self):
d.clear()
d = {i: X(i) for i in range(8)}
def iter_and_mutate():
for result in d.iteritems():
if result[0] == 2:
d[2] = None # free d[2] --> X(2).__del__ was called
self.assertRaises(RuntimeError, iter_and_mutate)
from test import mapping_tests from test import mapping_tests
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
......
...@@ -500,6 +500,7 @@ Hans de Graaff ...@@ -500,6 +500,7 @@ Hans de Graaff
Tim Graham Tim Graham
Nathaniel Gray Nathaniel Gray
Eddy De Greef Eddy De Greef
Duane Griffin
Grant Griffin Grant Griffin
Andrea Griffini Andrea Griffini
Duncan Grisby Duncan Grisby
......
...@@ -10,6 +10,10 @@ What's New in Python 2.7.14? ...@@ -10,6 +10,10 @@ What's New in Python 2.7.14?
Core and Builtins Core and Builtins
----------------- -----------------
- bpo-27945: Fixed various segfaults with dict when input collections are
mutated during searching, inserting or comparing. Based on patches by
Duane Griffin and Tim Mitchell.
- bpo-25794: Fixed type.__setattr__() and type.__delattr__() for - bpo-25794: Fixed type.__setattr__() and type.__delattr__() for
non-interned or unicode attribute names. Based on patch by Eryk Sun. non-interned or unicode attribute names. Based on patch by Eryk Sun.
......
...@@ -1595,11 +1595,18 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) ...@@ -1595,11 +1595,18 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
/* Update/merge with this (key, value) pair. */ /* Update/merge with this (key, value) pair. */
key = PySequence_Fast_GET_ITEM(fast, 0); key = PySequence_Fast_GET_ITEM(fast, 0);
value = PySequence_Fast_GET_ITEM(fast, 1); value = PySequence_Fast_GET_ITEM(fast, 1);
Py_INCREF(key);
Py_INCREF(value);
if (override || PyDict_GetItem(d, key) == NULL) { if (override || PyDict_GetItem(d, key) == NULL) {
int status = PyDict_SetItem(d, key, value); int status = PyDict_SetItem(d, key, value);
if (status < 0) if (status < 0) {
Py_DECREF(key);
Py_DECREF(value);
goto Fail; goto Fail;
}
} }
Py_DECREF(key);
Py_DECREF(value);
Py_DECREF(fast); Py_DECREF(fast);
Py_DECREF(item); Py_DECREF(item);
} }
...@@ -1938,12 +1945,13 @@ dict_equal(PyDictObject *a, PyDictObject *b) ...@@ -1938,12 +1945,13 @@ dict_equal(PyDictObject *a, PyDictObject *b)
/* ditto for key */ /* ditto for key */
Py_INCREF(key); Py_INCREF(key);
bval = PyDict_GetItem((PyObject *)b, key); bval = PyDict_GetItem((PyObject *)b, key);
Py_DECREF(key);
if (bval == NULL) { if (bval == NULL) {
Py_DECREF(key);
Py_DECREF(aval); Py_DECREF(aval);
return 0; return 0;
} }
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
Py_DECREF(key);
Py_DECREF(aval); Py_DECREF(aval);
if (cmp <= 0) /* error or not equal */ if (cmp <= 0) /* error or not equal */
return cmp; return cmp;
...@@ -2743,7 +2751,7 @@ PyTypeObject PyDictIterValue_Type = { ...@@ -2743,7 +2751,7 @@ PyTypeObject PyDictIterValue_Type = {
static PyObject *dictiter_iternextitem(dictiterobject *di) static PyObject *dictiter_iternextitem(dictiterobject *di)
{ {
PyObject *key, *value, *result = di->di_result; PyObject *key, *value, *result;
register Py_ssize_t i, mask; register Py_ssize_t i, mask;
register PyDictEntry *ep; register PyDictEntry *ep;
PyDictObject *d = di->di_dict; PyDictObject *d = di->di_dict;
...@@ -2770,22 +2778,27 @@ static PyObject *dictiter_iternextitem(dictiterobject *di) ...@@ -2770,22 +2778,27 @@ static PyObject *dictiter_iternextitem(dictiterobject *di)
if (i > mask) if (i > mask)
goto fail; goto fail;
if (result->ob_refcnt == 1) { di->len--;
key = ep[i].me_key;
value = ep[i].me_value;
Py_INCREF(key);
Py_INCREF(value);
result = di->di_result;
if (Py_REFCNT(result) == 1) {
PyObject *oldkey = PyTuple_GET_ITEM(result, 0);
PyObject *oldvalue = PyTuple_GET_ITEM(result, 1);
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
Py_INCREF(result); Py_INCREF(result);
Py_DECREF(PyTuple_GET_ITEM(result, 0)); Py_DECREF(oldkey);
Py_DECREF(PyTuple_GET_ITEM(result, 1)); Py_DECREF(oldvalue);
} else { } else {
result = PyTuple_New(2); result = PyTuple_New(2);
if (result == NULL) if (result == NULL)
return NULL; return NULL;
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
} }
di->len--;
key = ep[i].me_key;
value = ep[i].me_value;
Py_INCREF(key);
Py_INCREF(value);
PyTuple_SET_ITEM(result, 0, key);
PyTuple_SET_ITEM(result, 1, value);
return result; return result;
fail: fail:
...@@ -3186,6 +3199,7 @@ dictitems_iter(dictviewobject *dv) ...@@ -3186,6 +3199,7 @@ dictitems_iter(dictviewobject *dv)
static int static int
dictitems_contains(dictviewobject *dv, PyObject *obj) dictitems_contains(dictviewobject *dv, PyObject *obj)
{ {
int result;
PyObject *key, *value, *found; PyObject *key, *value, *found;
if (dv->dv_dict == NULL) if (dv->dv_dict == NULL)
return 0; return 0;
...@@ -3199,7 +3213,10 @@ dictitems_contains(dictviewobject *dv, PyObject *obj) ...@@ -3199,7 +3213,10 @@ dictitems_contains(dictviewobject *dv, PyObject *obj)
return -1; return -1;
return 0; return 0;
} }
return PyObject_RichCompareBool(value, found, Py_EQ); Py_INCREF(found);
result = PyObject_RichCompareBool(value, found, Py_EQ);
Py_DECREF(found);
return result;
} }
static PySequenceMethods dictitems_as_sequence = { static PySequenceMethods dictitems_as_sequence = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment