| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -976,6 +976,92 @@ def test_free_after_iterating(self): | |||
| 976 | 976 | support.check_free_after_iterating(self, lambda d: iter(d.values()), dict) | |
| 977 | 977 | support.check_free_after_iterating(self, lambda d: iter(d.items()), dict) | |
| 978 | 978 | ||
| 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 | + | ||
| 979 | 1065 | from test import mapping_tests | |
| 980 | 1066 | ||
| 981 | 1067 | class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -534,6 +534,7 @@ Tim Graham | |||
| 534 | 534 | Kim Gräsman | |
| 535 | 535 | Nathaniel Gray | |
| 536 | 536 | Eddy De Greef | |
| 537 | + Duane Griffin | ||
| 537 | 538 | Grant Griffin | |
| 538 | 539 | Andrea Griffini | |
| 539 | 540 | Duncan Grisby | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -10,6 +10,10 @@ Release date: XXXX-XX-XX | |||
| 10 | 10 | Core and Builtins | |
| 11 | 11 | ----------------- | |
| 12 | 12 | ||
| 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 | + | ||
| 13 | 17 | - bpo-25794: Fixed type.__setattr__() and type.__delattr__() for | |
| 14 | 18 | non-interned attribute names. Based on patch by Eryk Sun. | |
| 15 | 19 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -787,56 +787,61 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) | |||
| 787 | 787 | PyDictKeyEntry *ep; | |
| 788 | 788 | assert(key != dummy); | |
| 789 | 789 | ||
| 790 | + Py_INCREF(key); | ||
| 791 | + Py_INCREF(value); | ||
| 790 | 792 | if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { | |
| 791 | 793 | if (insertion_resize(mp) < 0) | |
| 792 | - return -1; | ||
| 794 | + goto Fail; | ||
| 793 | 795 | } | |
| 794 | 796 | ||
| 795 | 797 | 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 | + | ||
| 799 | 801 | assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); | |
| 800 | - Py_INCREF(value); | ||
| 801 | 802 | MAINTAIN_TRACKING(mp, key, value); | |
| 802 | 803 | old_value = *value_addr; | |
| 803 | 804 | if (old_value != NULL) { | |
| 804 | 805 | assert(ep->me_key != NULL && ep->me_key != dummy); | |
| 805 | 806 | *value_addr = value; | |
| 806 | 807 | Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ | |
| 808 | + Py_DECREF(key); | ||
| 807 | 809 | } | |
| 808 | 810 | else { | |
| 809 | 811 | if (ep->me_key == NULL) { | |
| 810 | - Py_INCREF(key); | ||
| 811 | 812 | if (mp->ma_keys->dk_usable <= 0) { | |
| 812 | 813 | /* 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; | ||
| 818 | 816 | ep = find_empty_slot(mp, key, hash, &value_addr); | |
| 819 | 817 | } | |
| 818 | + mp->ma_used++; | ||
| 819 | + *value_addr = value; | ||
| 820 | 820 | mp->ma_keys->dk_usable--; | |
| 821 | 821 | assert(mp->ma_keys->dk_usable >= 0); | |
| 822 | 822 | ep->me_key = key; | |
| 823 | 823 | ep->me_hash = hash; | |
| 824 | + assert(ep->me_key != NULL && ep->me_key != dummy); | ||
| 824 | 825 | } | |
| 825 | 826 | else { | |
| 827 | + mp->ma_used++; | ||
| 828 | + *value_addr = value; | ||
| 826 | 829 | if (ep->me_key == dummy) { | |
| 827 | - Py_INCREF(key); | ||
| 828 | 830 | ep->me_key = key; | |
| 829 | 831 | ep->me_hash = hash; | |
| 830 | 832 | Py_DECREF(dummy); | |
| 831 | 833 | } else { | |
| 832 | 834 | assert(_PyDict_HasSplitTable(mp)); | |
| 835 | + Py_DECREF(key); | ||
| 833 | 836 | } | |
| 834 | 837 | } | |
| 835 | - mp->ma_used++; | ||
| 836 | - *value_addr = value; | ||
| 837 | - assert(ep->me_key != NULL && ep->me_key != dummy); | ||
| 838 | 838 | } | |
| 839 | 839 | return 0; | |
| 840 | + | ||
| 841 | + Fail: | ||
| 842 | + Py_DECREF(value); | ||
| 843 | + Py_DECREF(key); | ||
| 844 | + return -1; | ||
| 840 | 845 | } | |
| 841 | 846 | ||
| 842 | 847 | /* | |
@@ -2057,11 +2062,18 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) | |||
| 2057 | 2062 | /* Update/merge with this (key, value) pair. */ | |
| 2058 | 2063 | key = PySequence_Fast_GET_ITEM(fast, 0); | |
| 2059 | 2064 | value = PySequence_Fast_GET_ITEM(fast, 1); | |
| 2065 | + Py_INCREF(key); | ||
| 2066 | + Py_INCREF(value); | ||
| 2060 | 2067 | if (override || PyDict_GetItem(d, key) == NULL) { | |
| 2061 | 2068 | int status = PyDict_SetItem(d, key, value); | |
| 2062 | - if (status < 0) | ||
| 2069 | + if (status < 0) { | ||
| 2070 | + Py_DECREF(key); | ||
| 2071 | + Py_DECREF(value); | ||
| 2063 | 2072 | goto Fail; | |
| 2073 | + } | ||
| 2064 | 2074 | } | |
| 2075 | + Py_DECREF(key); | ||
| 2076 | + Py_DECREF(value); | ||
| 2065 | 2077 | Py_DECREF(fast); | |
| 2066 | 2078 | Py_DECREF(item); | |
| 2067 | 2079 | } | |
@@ -2320,14 +2332,15 @@ dict_equal(PyDictObject *a, PyDictObject *b) | |||
| 2320 | 2332 | bval = NULL; | |
| 2321 | 2333 | else | |
| 2322 | 2334 | bval = *vaddr; | |
| 2323 | - Py_DECREF(key); | ||
| 2324 | 2335 | if (bval == NULL) { | |
| 2336 | + Py_DECREF(key); | ||
| 2325 | 2337 | Py_DECREF(aval); | |
| 2326 | 2338 | if (PyErr_Occurred()) | |
| 2327 | 2339 | return -1; | |
| 2328 | 2340 | return 0; | |
| 2329 | 2341 | } | |
| 2330 | 2342 | cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); | |
| 2343 | + Py_DECREF(key); | ||
| 2331 | 2344 | Py_DECREF(aval); | |
| 2332 | 2345 | if (cmp <= 0) /* error or not equal */ | |
| 2333 | 2346 | return cmp; | |
@@ -3152,7 +3165,7 @@ PyTypeObject PyDictIterValue_Type = { | |||
| 3152 | 3165 | ||
| 3153 | 3166 | static PyObject *dictiter_iternextitem(dictiterobject *di) | |
| 3154 | 3167 | { | |
| 3155 | - PyObject *key, *value, *result = di->di_result; | ||
| 3168 | + PyObject *key, *value, *result; | ||
| 3156 | 3169 | Py_ssize_t i, mask, offset; | |
| 3157 | 3170 | PyDictObject *d = di->di_dict; | |
| 3158 | 3171 | PyObject **value_ptr; | |
@@ -3188,22 +3201,27 @@ static PyObject *dictiter_iternextitem(dictiterobject *di) | |||
| 3188 | 3201 | if (i > mask) | |
| 3189 | 3202 | goto fail; | |
| 3190 | 3203 | ||
| 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 */ | ||
| 3192 | 3215 | 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); | ||
| 3195 | 3218 | } else { | |
| 3196 | 3219 | result = PyTuple_New(2); | |
| 3197 | 3220 | if (result == NULL) | |
| 3198 | 3221 | return NULL; | |
| 3222 | + PyTuple_SET_ITEM(result, 0, key); /* steals reference */ | ||
| 3223 | + PyTuple_SET_ITEM(result, 1, value); /* steals reference */ | ||
| 3199 | 3224 | } | |
| 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 */ | ||
| 3207 | 3225 | return result; | |
| 3208 | 3226 | ||
| 3209 | 3227 | fail: | |
@@ -3696,6 +3714,7 @@ dictitems_iter(_PyDictViewObject *dv) | |||
| 3696 | 3714 | static int | |
| 3697 | 3715 | dictitems_contains(_PyDictViewObject *dv, PyObject *obj) | |
| 3698 | 3716 | { | |
| 3717 | + int result; | ||
| 3699 | 3718 | PyObject *key, *value, *found; | |
| 3700 | 3719 | if (dv->dv_dict == NULL) | |
| 3701 | 3720 | return 0; | |
@@ -3709,7 +3728,10 @@ dictitems_contains(_PyDictViewObject *dv, PyObject *obj) | |||
| 3709 | 3728 | return -1; | |
| 3710 | 3729 | return 0; | |
| 3711 | 3730 | } | |
| 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; | ||
| 3713 | 3735 | } | |
| 3714 | 3736 | ||
| 3715 | 3737 | static PySequenceMethods dictitems_as_sequence = { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments