8000 bpo-27945: Fixed various segfaults with dict. (#1657) · stackless-dev/stackless@753bca3 · 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 753bca3

Browse files
bpo-27945: Fixed various segfaults with dict. (python#1657)
Based on patches by Duane Griffin and Tim Mitchell.
1 parent 763557e commit 753bca3

File tree

4 files changed

+133
-25
lines changed

4 files changed

+133
-25
lines changed

Lib/test/test_dict.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,91 @@ def test_free_after_iterating(self):
10851085
support.check_free_after_iterating(self, lambda d: iter(d.values()), dict)
10861086
support.check_free_after_iterating(self, lambda d: iter(d.items()), dict)
10871087

1088+
def test_equal_operator_modifying_operand(self):
1089+
# test fix for seg fault reported in issue 27945 part 3.
1090+
class X():
1091+
def __del__(self):
1092+
dict_b.clear()
1093+
1094+
def __eq__(self, other):
1095+
dict_a.clear()
1096+
return True
1097+
1098+
def __hash__(self):
1099+
return 13
1100+
1101+
dict_a = {X(): 0}
1102+
dict_b = {X(): X()}
1103+
self.assertTrue(dict_a == dict_b)
1104+
1105+
def test_fromkeys_operator_modifying_dict_operand(self):
1106+
# test fix for seg fault reported in issue 27945 part 4a.
1107+
class X(int):
1108+
def __hash__(self):
1109+
return 13
1110+
1111+
def __eq__(self, other):
1112+
if len(d) > 1:
1113+
d.clear()
1114+
return False
1115+
1116+
d = {} # this is required to exist so that d can be constructed!
1117+
d = {X(1): 1, X(2): 2}
1118+
try:
1119+
dict.fromkeys(d) # shouldn't crash
1120+
except RuntimeError: # implementation defined
1121+
pass
1122+
1123+
def test_fromkeys_operator_modifying_set_operand(self):
1124+
# test fix for seg fault reported in issue 27945 part 4b.
1125+
class X(int):
1126+
def __hash__(self):
1127+
return 13
1128+
1129+
def __eq__(self, other):
1130+
if len(d) > 1:
1131+
d.clear()
1132+
return False
1133+
1134+
d = {} # this is required to exist so that d can be constructed!
1135+
d = {X(1), X(2)}
1136+
try:
1137+
dict.fromkeys(d) # shouldn't crash
1138+
except RuntimeError: # implementation defined
1139+
pass
1140+
1141+
def test_dictitems_contains_use_after_free(self):
1142+
class X:
1143+
def __eq__(self, other):
1144+
d.clear()
1145+
return NotImplemented
1146+
1147+
d = {0: set()}
1148+
(0, X()) in d.items()
1149+
1150+
def test_init_use_after_free(self):
1151+
class X:
1152+
def __hash__(self):
1153+
pair[:] = []
1154+
return 13
1155+
1156+
pair = [X(), 123]
1157+
dict([pair])
1158+
1159+
def test_oob_indexing_dictiter_iternextitem(self):
1160+
class X(int):
1161+
def __del__(self):
1162+
d.clear()
1163+
1164+
d = {i: X(i) for i in range(8)}
1165+
1166+
def iter_and_mutate():
1167+
for result in d.items():
1168+
if result[0] == 2:
1169+
d[2] = None # free d[2] --> X(2).__del__ was called
1170+
1171+
self.assertRaises(RuntimeError, iter_and_mutate)
1172+
10881173

10891174
class CAPITest(unittest.TestCase):
10901175

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ Tim Graham
546546
Kim Gräsman
547547
Nathaniel Gray
548548
Eddy De Greef
549+
Duane Griffin
549550
Grant Griffin
550551
Andrea Griffini
551552
Duncan Grisby

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ What's New in Python 3.7.0 alpha 1?
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: 43 additions & 25 deletions
F438
Original file line numberDiff line numberDiff line change
@@ -1107,18 +1107,18 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
11071107
PyDictKeyEntry *ep;
11081108
Py_ssize_t hashpos, ix;
11091109

1110+
Py_INCREF(key);
1111+
Py_INCREF(value);
11101112
if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) {
11111113
if (insertion_resize(mp) < 0)
1112-
return -1;
1114+
goto Fail;
11131115
}
11141116

11151117
ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value, &hashpos);
1116-
if (ix == DKIX_ERROR) {
1117-
return -1;
1118-
}
1118+
if (ix == DKIX_ERROR)
1119+
goto Fail;
11191120

11201121
assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
1121-
Py_INCREF(value);
11221122
MAINTAIN_TRACKING(mp, key, value);
11231123

11241124
/* 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)
11271127
if (_PyDict_HasSplitTable(mp) &&
11281128
((ix >= 0 && old_value == NULL && mp->ma_used != ix) ||
11291129
(ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) {
1130-
if (insertion_resize(mp) < 0) {
1131-
Py_DECREF(value);
1132-
return -1;
1133-
}
1130+
if (insertion_resize(mp) < 0)
1131+
goto Fail;
11341132
hashpos = find_empty_slot(mp->ma_keys, key, hash);
11351133
ix = DKIX_EMPTY;
11361134
}
@@ -1140,15 +1138,12 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
11401138
assert(old_value == NULL);
11411139
if (mp->ma_keys->dk_usable <= 0) {
11421140
/* Need to resize. */
1143-
if (insertion_resize(mp) < 0) {
1144-
Py_DECREF(value);
1145-
return -1;
1146-
}
1141+
if (insertion_resize(mp) < 0)
1142+
goto Fail;
11471143
hashpos = find_empty_slot(mp->ma_keys, key, hash);
11481144
}
11491145
ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
11501146
dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);
1151-
Py_INCREF(key);
11521147
ep->me_key = key;
11531148
ep->me_hash = hash;
11541149
if (mp->ma_values) {
@@ -1183,7 +1178,13 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
11831178
mp->ma_version_tag = DICT_NEXT_VERSION();
11841179
Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
11851180
assert(_PyDict_CheckConsistency(mp));
1181+
Py_DECREF(key);
11861182
return 0;
1183+
1184+
Fail:
1185+
Py_DECREF(value);
1186+
Py_DECREF(key);
1187+
return -1;
11871188
}
11881189

11891190
/*
@@ -2419,11 +2420,18 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
24192420
/* Update/merge with this (key, value) pair. */
24202421
key = PySequence_Fast_GET_ITEM(fast, 0);
24212422
value = PySequence_Fast_GET_ITEM(fast, 1);
2423+
Py_INCREF(key);
2424+
Py_INCREF(value);
24222425
if (override || PyDict_GetItem(d, key) == NULL) {
24232426
int status = PyDict_SetItem(d, key, value);
2424-
if (status < 0)
2427+
if (status < 0) {
2428+
Py_DECREF(key);
2429+
Py_DECREF(value);
24252430
goto Fail;
2431+
}
24262432
}
2433+
Py_DECREF(key);
2434+
Py_DECREF(value);
24272435
Py_DECREF(fast);
24282436
Py_DECREF(item);
24292437
}
@@ -2720,14 +2728,15 @@ dict_equal(PyDictObject *a, PyDictObject *b)
27202728
Py_INCREF(key);
27212729
/* reuse the known hash value */
27222730
b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval, NULL);
2723-
Py_DECREF(key);
27242731
if (bval == NULL) {
2732+
Py_DECREF(key);
27252733
Py_DECREF(aval);
27262734
if (PyErr_Occurred())
27272735
return -1;
27282736
return 0;
27292737
}
27302738
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
2739+
Py_DECREF(key);
27312740
Py_DECREF(aval);
27322741
if (cmp <= 0) /* error or not equal */
27332742
return cmp;
@@ -3612,7 +3621,7 @@ PyTypeObject PyDictIterValue_Type = {
36123621
static PyObject *
36133622
dictiter_iternextitem(dictiterobject *di)
36143623
{
3615-
PyObject *key, *value, *result = di->di_result;
3624+
PyObject *key, *value, *result;
36163625
Py_ssize_t i;
36173626
PyDictObject *d = di->di_dict;
36183627

@@ -3650,20 +3659,25 @@ dictiter_iternextitem(dictiterobject *di)
36503659
}
36513660
di->di_pos = i+1;
36523661
di->len--;
3653-
if (result->ob_refcnt == 1) {
3662+
Py_INCREF(key);
3663+
Py_INCREF(value);
3664+
result = di->di_result;
3665+
if (Py_REFCNT(result) == 1) {
3666+
PyObject *oldkey = PyTuple_GET_ITEM(result, 0);
3667+
PyObject *oldvalue = PyTuple_GET_ITEM(result, 1);
3668+
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
3669+
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
36543670
Py_INCREF(result);
3655-
Py_DECREF(PyTuple_GET_ITEM(result, 0));
3656-
Py_DECREF(PyTuple_GET_ITEM(result, 1));
3671+
Py_DECREF(oldkey);
3672+
Py_DECREF(oldvalue);
36573673
}
36583674
else {
36593675
result = PyTuple_New(2);
36603676
if (result == NULL)
36613677
return NULL;
3678+
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
3679+
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
36623680
}
3663-
Py_INCREF(key);
3664-
Py_INCREF(value);
3665-
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
3666-
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
36673681
return result;
36683682

36693683
fail:
@@ -4156,6 +4170,7 @@ dictitems_iter(_PyDictViewObject *dv)
41564170
static int
41574171
dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
41584172
{
4173+
int result;
41594174
PyObject *key, *value, *found;
41604175
if (dv->dv_dict == NULL)
41614176
return 0;
@@ -4169,7 +4184,10 @@ dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
41694184
return -1;
41704185
return 0;
41714186
}
4172-
return PyObject_RichCompareBool(value, found, Py_EQ);
4187+
Py_INCREF(found);
4188+
result = PyObject_RichCompareBool(value, found, Py_EQ);
4189+
Py_DECREF(found);
4190+
return result;
41734191
}
41744192

41754193
static PySequenceMethods dictitems_as_sequence = {

0 commit comments

Comments
 (0)
0