Kaydet (Commit) 753bca39 authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka Kaydeden (comit) GitHub

bpo-27945: Fixed various segfaults with dict. (#1657)

Based on patches by Duane Griffin and Tim Mitchell.
üst 763557ea
...@@ -1085,6 +1085,91 @@ class DictTest(unittest.TestCase): ...@@ -1085,6 +1085,91 @@ class DictTest(unittest.TestCase):
support.check_free_after_iterating(self, lambda d: iter(d.values()), dict) support.check_free_after_iterating(self, lambda d: iter(d.values()), dict)
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):
# test fix for seg fault reported in issue 27945 part 3.
class X():
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:
def __eq__(self, other):
d.clear()
return NotImplemented
d = {0: set()}
(0, X()) in d.items()
def test_init_use_after_free(self):
class X:
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.items():
if result[0] == 2:
d[2] = None # free d[2] --> X(2).__del__ was called
self.assertRaises(RuntimeError, iter_and_mutate)
class CAPITest(unittest.TestCase): class CAPITest(unittest.TestCase):
......
...@@ -546,6 +546,7 @@ Tim Graham ...@@ -546,6 +546,7 @@ Tim Graham
Kim Gräsman Kim Gräsman
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 3.7.0 alpha 1? ...@@ -10,6 +10,10 @@ What's New in Python 3.7.0 alpha 1?
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 attribute names. Based on patch by Eryk Sun. non-interned attribute names. Based on patch by Eryk Sun.
......
...@@ -1107,18 +1107,18 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) ...@@ -1107,18 +1107,18 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
PyDictKeyEntry *ep; PyDictKeyEntry *ep;
Py_ssize_t hashpos, ix; Py_ssize_t hashpos, ix;
Py_INCREF(key);
Py_INCREF(value);
if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) {
if (insertion_resize(mp) < 0) if (insertion_resize(mp) < 0)
return -1; goto Fail;
} }
ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value, &hashpos); ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value, &hashpos);
if (ix == DKIX_ERROR) { if (ix == DKIX_ERROR)
return -1; goto Fail;
}
assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
Py_INCREF(value);
MAINTAIN_TRACKING(mp, key, value); MAINTAIN_TRACKING(mp, key, value);
/* When insertion order is different from shared key, we can't share /* When insertion order is different from shared key, we can't share
...@@ -1127,10 +1127,8 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) ...@@ -1127,10 +1127,8 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
if (_PyDict_HasSplitTable(mp) && if (_PyDict_HasSplitTable(mp) &&
((ix >= 0 && old_value == NULL && mp->ma_used != ix) || ((ix >= 0 && old_value == NULL && mp->ma_used != ix) ||
(ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) {
if (insertion_resize(mp) < 0) { if (insertion_resize(mp) < 0)
Py_DECREF(value); goto Fail;
return -1;
}
hashpos = find_empty_slot(mp->ma_keys, key, hash); hashpos = find_empty_slot(mp->ma_keys, key, hash);
ix = DKIX_EMPTY; ix = DKIX_EMPTY;
} }
...@@ -1140,15 +1138,12 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) ...@@ -1140,15 +1138,12 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
assert(old_value == NULL); assert(old_value == NULL);
if (mp->ma_keys->dk_usable <= 0) { if (mp->ma_keys->dk_usable <= 0) {
/* Need to resize. */ /* Need to resize. */
if (insertion_resize(mp) < 0) { if (insertion_resize(mp) < 0)
Py_DECREF(value); goto Fail;
return -1;
}
hashpos = find_empty_slot(mp->ma_keys, key, hash); hashpos = find_empty_slot(mp->ma_keys, key, hash);
} }
ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries]; ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);
Py_INCREF(key);
ep->me_key = key; ep->me_key = key;
ep->me_hash = hash; ep->me_hash = hash;
if (mp->ma_values) { if (mp->ma_values) {
...@@ -1183,7 +1178,13 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) ...@@ -1183,7 +1178,13 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_version_tag = DICT_NEXT_VERSION();
Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
assert(_PyDict_CheckConsistency(mp)); assert(_PyDict_CheckConsistency(mp));
Py_DECREF(key);
return 0; return 0;
Fail:
Py_DECREF(value);
Py_DECREF(key);
return -1;
} }
/* /*
...@@ -2419,11 +2420,18 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) ...@@ -2419,11 +2420,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);
} }
...@@ -2720,14 +2728,15 @@ dict_equal(PyDictObject *a, PyDictObject *b) ...@@ -2720,14 +2728,15 @@ dict_equal(PyDictObject *a, PyDictObject *b)
Py_INCREF(key); Py_INCREF(key);
/* reuse the known hash value */ /* reuse the known hash value */
b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval, NULL); b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval, NULL);
Py_DECREF(key);
if (bval == NULL) { if (bval == NULL) {
Py_DECREF(key);
Py_DECREF(aval); Py_DECREF(aval);
if (PyErr_Occurred()) if (PyErr_Occurred())
return -1; return -1;
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;
...@@ -3612,7 +3621,7 @@ PyTypeObject PyDictIterValue_Type = { ...@@ -3612,7 +3621,7 @@ PyTypeObject PyDictIterValue_Type = {
static PyObject * static PyObject *
dictiter_iternextitem(dictiterobject *di) dictiter_iternextitem(dictiterobject *di)
{ {
PyObject *key, *value, *result = di->di_result; PyObject *key, *value, *result;
Py_ssize_t i; Py_ssize_t i;
PyDictObject *d = di->di_dict; PyDictObject *d = di->di_dict;
...@@ -3650,20 +3659,25 @@ dictiter_iternextitem(dictiterobject *di) ...@@ -3650,20 +3659,25 @@ dictiter_iternextitem(dictiterobject *di)
} }
di->di_pos = i+1; di->di_pos = i+1;
di->len--; di->len--;
if (result->ob_refcnt == 1) { 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 */
} }
Py_INCREF(key);
Py_INCREF(value);
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
return result; return result;
fail: fail:
...@@ -4156,6 +4170,7 @@ dictitems_iter(_PyDictViewObject *dv) ...@@ -4156,6 +4170,7 @@ dictitems_iter(_PyDictViewObject *dv)
static int static int
dictitems_contains(_PyDictViewObject *dv, PyObject *obj) dictitems_contains(_PyDictViewObject *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;
...@@ -4169,7 +4184,10 @@ dictitems_contains(_PyDictViewObject *dv, PyObject *obj) ...@@ -4169,7 +4184,10 @@ dictitems_contains(_PyDictViewObject *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