bpo-27945: Fixed various segfaults with dict. (#1657)
Based on patches by Duane Griffin and Tim Mitchell.
This commit is contained in:
parent
763557eac0
commit
753bca3934
@ -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
|
|||||||
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?
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
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 = {
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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;
|
||||||
}
|
|
||||||
Py_INCREF(key);
|
|
||||||
Py_INCREF(value);
|
|
||||||
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
|
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
|
||||||
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
|
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -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)
|
|||||||
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 = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user