8000 [3.5] bpo-27945: Fixed various segfaults with dict. (GH-1657) (#1678) · stackless-dev/stackless@2f7f533 · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 2f7f533

Browse files
[3.5] bpo-27945: Fixed various segfaults with dict. (pythonGH-1657) (python#1678)
Based on patches by Duane Griffin and Tim Mitchell. (cherry picked from commit 753bca3)
1 parent 4a86fe9 commit 2f7f533

File tree

4 files changed

+142
-29
lines changed

4 files changed

+142
-29
lines changed

Lib/test/test_dict.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,92 @@ def test_free_after_iterating(self):
976976
support.check_free_after_iterating(self, lambda d: iter(d.values()), dict)
977977
support.check_free_after_iterating(self, lambda d: iter(d.items()), dict)
978978

979+
def test_equal_operator_modifying_operand(self):
980+
# test fix for seg fault reported in issue 27945 part 3.
981+
class X():
982+
def __del__(self):
983+
dict_b.clear()
984+
985+
def __eq__(self, other):
986+
dict_a.clear()
987+
return True
988+
989+
def __hash__(self):
990+
return 13
991+
992+
dict_a = {X(): 0}
993+
dict_b = {X(): X()}
994+
self.assertTrue(dict_a == dict_b)
995+
996+
def test_fromkeys_operator_modifying_dict_operand(self):
997+
# test fix for seg fault reported in issue 27945 part 4a.
998+
class X(int):
999+
def __hash__(self):
1000+
return 13
1001+
1002+
def __eq__(self, other):
1003+
if len(d) > 1:
1004+
d.clear()
1005+
return False
1006+
1007+
d = {} # this is required to exist so that d can be constructed!
1008+
d = {X(1): 1, X(2): 2}
1009+
try:
1010+
dict.fromkeys(d) # shouldn't crash
1011+
except RuntimeError: # implementation defined
1012+
pass
1013+
1014+
def test_fromkeys_operator_modifying_set_operand(self):
1015+
# test fix for seg fault reported in issue 27945 part 4b.
1016+
class X(int):
1017+
def __hash__(self):
1018+
return 13
1019+
1020+
def __eq__(self, other):
1021+
if len(d) > 1:
1022+
d.clear()
1023+
return False
1024+
1025+
d = {} # this is required to exist so that d can be constructed!
1026+
d = {X(1), X(2)}
1027+
try:
1028+
dict.fromkeys(d) # shouldn't crash
1029+
except RuntimeError: # implementation defined
1030+
pass
1031+
1032+
def test_dictitems_contains_use_after_free(self):
1033+
class X:
1034+
def __eq__(self, other):
1035+
d.clear()
1036+
return NotImplemented
1037+
1038+
d = {0: set()}
1039+
(0, X()) in d.items()
1040+
1041+
def test_init_use_after_free(self):
1042+
class X:
1043+
def __hash__(self):
1044+
pair[:] = []
1045+
return 13
1046+
1047+
pair = [X(), 123]
1048+
dict([pair])
1049+
1050+
def test_oob_indexing_dictiter_iternextitem(self):
1051+
class X(int):
1052+
def __del__(self):
1053+
d.clear()
1054+
1055+
d = {i: X(i) for i in range(8)}
1056+
1057+
def iter_and_mutate():
1058+
for result in d.items():
1059+
if result[0] == 2:
1060+
d[2] = None # free d[2] --> X(2).__del__ was called
1061+
1062+
self.assertRaises(RuntimeError, iter_and_mutate)
1063+
1064+
9791065
from test import mapping_tests
9801066

9811067
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ Tim Graham
534534
Kim Gräsman
535535
Nathaniel Gray
536536
Eddy De Greef
537+
Duane Griffin
537538
Grant Griffin
538539
Andrea Griffini
539540
Duncan Grisby

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Release date: XXXX-XX-XX
1010
Core and Builtins
1111
-----------------
1212

13+
- bpo-27945: Fixed various segfaults with dict when input collections are
14+
mutated during searching, inserting or comparing. Based on patches by
15+
Duane Griffin and Tim Mitchell.
16+
1317
- bpo-25794: Fixed type.__setattr__() and type.__delattr__() for
1418
non-interned attribute names. Based on patch by Eryk Sun.
1519

Objects/dictobject.c

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -787,56 +787,61 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
787787
PyDictKeyEntry *ep;
788788
assert(key != dummy);
789789

790+
Py_INCREF(key);
791+
Py_INCREF(value);
790792
if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) {
791793
if (insertion_resize(mp) < 0)
792-
return -1;
794+
goto Fail;
793795
}
794796

795797
ep = mp->ma_keys->dk_lookup(mp, key, hash, &value_addr);
796-
if (ep == NULL) {
797-
return -1;
798-
}
798+
if (ep == NULL)
799+
goto Fail;
800+
799801
assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
800-
Py_INCREF(value);
801802
MAINTAIN_TRACKING(mp, key, value);
802803
old_value = *value_addr;
803804
if (old_value != NULL) {
804805
assert(ep->me_key != NULL && ep->me_key != dummy);
805806
*value_addr = value;
806807
Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
808+
Py_DECREF(key);
807809
}
808810
else {
809811
if (ep->me_key == NULL) {
810-
Py_INCREF(key);
811812
if (mp->ma_keys->dk_usable <= 0) {
812813
/* Need to resize. */
813-
if (insertion_resize(mp) < 0) {
814-
Py_DECREF(key);
815-
Py_DECREF(value);
816-
return -1;
817-
}
814+
if (insertion_resize(mp) < 0)
815+
goto Fail;
818816
ep = find_empty_slot(mp, key, hash, &value_addr);
819817
}
818+
mp->ma_used++;
819+
*value_addr = value;
820820
mp->ma_keys->dk_usable--;
821821
assert(mp->ma_keys->dk_usable >= 0);
822822
ep->me_key = key;
823823
ep->me_hash = hash;
824+
assert(ep->me_key != NULL && ep->me_key != dummy);
824825
}
825826
else {
827+
mp->ma_used++;
828+
*value_addr = value;
826829
if (ep->me_key == dummy) {
827-
Py_INCREF(key);
828830
ep->me_key = key;
829831
ep->me_hash = hash;
830832
Py_DECREF(dummy);
831833
} else {
832834
assert(_PyDict_HasSplitTable(mp));
835+
Py_DECREF(key);
833836
}
834837
}
835-
mp->ma_used++;
836-
*value_addr = value;
837-
assert(ep->me_key != NULL && ep->me_key != dummy);
838838
}
839839
return 0;
840+
841+
Fail:
842+
Py_DECREF(value);
843+
Py_DECREF(key);
844+
return -1;
840845
}
841846

842847
/*
@@ -2057,11 +2062,18 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
20572062
/* Update/merge with this (key, value) pair. */
20582063
key = PySequence_Fast_GET_ITEM(fast, 0);
20592064
value = PySequence_Fast_GET_ITEM(fast, 1);
2065+
Py_INCREF(key);
2066+
Py_INCREF(value);
20602067
if (override || PyDict_GetItem(d, key) == NULL) {
20612068
int status = PyDict_SetItem(d, key, value);
2062-
if (status < 0)
2069+
if (status < 0) {
2070+
Py_DECREF(key);
2071+
Py_DECREF(value);
20632072
goto Fail;
2073+
}
20642074
}
2075+
Py_DECREF(key);
2076+
Py_DECREF(value);
20652077
Py_DECREF(fast);
20662078
Py_DECREF(item);
20672079
}
@@ -2320,14 +2332,15 @@ dict_equal(PyDictObject *a, PyDictObject *b)
23202332
bval = NULL;
23212333
else
23222334
bval = *vaddr;
2323-
Py_DECREF(key);
23242335
if (bval == NULL) {
2336+
Py_DECREF(key);
23252337
Py_DECREF(aval);
23262338
if (PyErr_Occurred())
23272339
return -1;
23282340
return 0;
23292341
}
23302342
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
2343+
Py_DECREF(key);
23312344
Py_DECREF(aval);
23322345
if (cmp <= 0) /* error or not equal */
23332346
return cmp;
@@ -3152,7 +3165,7 @@ PyTypeObject PyDictIterValue_Type = {
31523165

31533166
static PyObject *dictiter_iternextitem(dictiterobject *di)
31543167
{
3155-
PyObject *key, *value, *result = di->di_result;
3168+
PyObject *key, *value, *result;
31563169
Py_ssize_t i, mask, offset;
31573170
PyDictObject *d = di->di_dict;
31583171
PyObject **value_ptr;
@@ -3188,22 +3201,27 @@ static PyObject *dictiter_iternextitem(dictiterobject *di)
31883201
if (i > mask)
31893202
goto fail;
31903203

3191-
if (result->ob_refcnt == 1) {
3204+
di->len--;
3205+
key = d->ma_keys->dk_entries[i].me_key;
3206+
value = *value_ptr;
3207+
Py_INCREF(key);
3208+
Py_INCREF(value);
3209+
result = di->di_result;
3210+
if (Py_REFCNT(result) == 1) {
3211+
PyObject *oldkey = PyTuple_GET_ITEM(result, 0);
3212+
PyObject *oldvalue = PyTuple_GET_ITEM(result, 1);
3213+
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
3214+
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
31923215
Py_INCREF(result);
3193-
Py_DECREF(PyTuple_GET_ITEM(result, 0));
3194-
Py_DECREF(PyTuple_GET_ITEM(result, 1));
3216+
Py_DECREF(oldkey);
3217+
Py_DECREF(oldvalue);
31953218
} else {
31963219
result = PyTuple_New(2);
31973220
if (result == NULL)
31983221
return NULL;
3222+
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
3223+
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
31993224
}
3200-
di->len--;
3201-
key = d->ma_keys->dk_entries[i].me_key;
3202-
value = *value_ptr;
3203-
Py_INCREF(key);
3204-
Py_INCREF(value);
3205-
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
3206-
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
32073225
return result;
32083226

32093227
fail:
@@ -3696,6 +3714,7 @@ dictitems_iter(_PyDictViewObject *dv)
36963714
static int
36973715
dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
36983716
{
3717+
int result;
36993718
PyObject *key, *value, *found;
37003719
if (dv->dv_dict == NULL)
37013720
return 0;
@@ -3709,7 +3728,10 @@ dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
37093728
return -1;
37103729
return 0;
37113730
}
3712-
return PyObject_RichCompareBool(value, found, Py_EQ);
3731+
Py_INCREF(found);
3732+
result = PyObject_RichCompareBool(value, found, Py_EQ);
3733+
Py_DECREF(found);
3734+
return result;
37133735
}
37143736

37153737
static PySequenceMethods dictitems_as_sequence = {

0 commit comments

Comments
 (0)
0