mirror of
https://github.com/python/cpython.git
synced 2026-05-04 09:31:02 +00:00
Merge dfcea68a8a into 3efd2f4db6
This commit is contained in:
commit
003f5bc8fe
3 changed files with 113 additions and 7 deletions
|
|
@ -976,6 +976,93 @@ def test_weakref_list_is_not_traversed(self):
|
|||
|
||||
gc.collect()
|
||||
|
||||
def test_copy_concurrent_clear_in__getitem__(self):
|
||||
# Prevent crashes when a copy mutates the OrderedDict.
|
||||
# Regression tests for github.com/python/cpython/issues/142734.
|
||||
class MyOD(self.OrderedDict):
|
||||
def __getitem__(self, key):
|
||||
super().clear()
|
||||
return None
|
||||
|
||||
od = MyOD(enumerate(range(5)))
|
||||
msg = "OrderedDict mutated during iteration"
|
||||
self.assertRaisesRegex(RuntimeError, msg, od.copy)
|
||||
|
||||
def test_copy_concurrent_insertion_in__getitem__(self):
|
||||
class MyOD(self.OrderedDict):
|
||||
def __getitem__(self, key):
|
||||
self['new_key'] = 'new_value'
|
||||
return super().__getitem__(key)
|
||||
|
||||
od = MyOD([(1, 'one'), (2, 'two')])
|
||||
msg = "OrderedDict mutated during iteration"
|
||||
self.assertRaisesRegex(RuntimeError, msg, od.copy)
|
||||
|
||||
def test_copy_concurrent_deletion_by_del_in__getitem__(self):
|
||||
class MyOD(self.OrderedDict):
|
||||
call_count = 0
|
||||
def __getitem__(self, key):
|
||||
self.call_count += 1
|
||||
if self.call_count == 1:
|
||||
del self[3]
|
||||
return super().__getitem__(key)
|
||||
|
||||
od = MyOD([(1, 'one'), (2, 'two'), (3, 'three')])
|
||||
msg = "OrderedDict mutated during iteration"
|
||||
self.assertRaisesRegex(RuntimeError, msg, od.copy)
|
||||
|
||||
def test_copy_concurrent_deletion_by_pop_in__getitem__(self):
|
||||
class MyOD(self.OrderedDict):
|
||||
call_count = 0
|
||||
def __getitem__(self, key):
|
||||
self.call_count += 1
|
||||
if self.call_count == 1:
|
||||
self.pop(3)
|
||||
return super().__getitem__(key)
|
||||
|
||||
od = MyOD([(1, 'one'), (2, 'two'), (3, 'three')])
|
||||
msg = "OrderedDict mutated during iteration"
|
||||
self.assertRaisesRegex(RuntimeError, msg, od.copy)
|
||||
|
||||
def test_copy_concurrent_deletion_and_set_in__getitem__(self):
|
||||
class MyOD(self.OrderedDict):
|
||||
call_count = 0
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __getitem__(self, key):
|
||||
self.call_count += 1
|
||||
if self.call_count == 1:
|
||||
del self[4]
|
||||
elif self.call_count == 2:
|
||||
self['new_key'] = 'new_value'
|
||||
return super().__getitem__(key)
|
||||
|
||||
od = MyOD([(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')])
|
||||
msg = "OrderedDict mutated during iteration"
|
||||
self.assertRaisesRegex(RuntimeError, msg, od.copy)
|
||||
|
||||
def test_copy_concurrent_mutation_in__setitem__(self):
|
||||
class MyOD(self.OrderedDict):
|
||||
# When calling `__getitem__` on the source dict, `instance_count` is 1.
|
||||
# When calling `__setitem__` on the target dict, `instance_count` is 2.
|
||||
instance_count = 0
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
MyOD.instance_count += 1
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if self.instance_count == 2 and len(od) > 1:
|
||||
del od[next(iter(od))]
|
||||
return super().__setitem__(key, value)
|
||||
|
||||
od = MyOD([(1, 'one'), (2, 'two'), (3, 'three')])
|
||||
msg = "OrderedDict mutated during iteration"
|
||||
self.assertRaisesRegex(RuntimeError, msg, od.copy)
|
||||
self.assertEqual(MyOD.instance_count, 2)
|
||||
|
||||
|
||||
class PurePythonOrderedDictSubclassTests(PurePythonOrderedDictTests):
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
:mod:`collections`: fix use-after-free crashes in :meth:`OrderedDict.copy <dict.copy>` when the dictionary to copy is concurrently mutated.
|
||||
|
|
@ -1265,21 +1265,39 @@ OrderedDict_copy_impl(PyObject *od)
|
|||
}
|
||||
}
|
||||
else {
|
||||
PyODictObject *self = _PyODictObject_CAST(od);
|
||||
size_t state = self->od_state;
|
||||
|
||||
_odict_FOREACH(od, node) {
|
||||
int res;
|
||||
PyObject *value = PyObject_GetItem((PyObject *)od,
|
||||
_odictnode_KEY(node));
|
||||
if (value == NULL)
|
||||
PyObject *key = Py_NewRef(_odictnode_KEY(node));
|
||||
PyObject *value = PyObject_GetItem(od, key);
|
||||
if (value == NULL) {
|
||||
Py_DECREF(key);
|
||||
goto fail;
|
||||
res = PyObject_SetItem((PyObject *)od_copy,
|
||||
_odictnode_KEY(node), value);
|
||||
}
|
||||
|
||||
if (self->od_state != state) {
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(value);
|
||||
goto invalid_state; // 成功获取值但状态改变
|
||||
}
|
||||
|
||||
int rc = PyObject_SetItem(od_copy, key, value);
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(value);
|
||||
if (res != 0)
|
||||
if (rc != 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (self->od_state != state) {
|
||||
goto invalid_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
return od_copy;
|
||||
|
||||
invalid_state:
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"OrderedDict mutated during iteration");
|
||||
fail:
|
||||
Py_DECREF(od_copy);
|
||||
return NULL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue