mirror of
https://github.com/python/cpython.git
synced 2025-12-23 09:19:18 +00:00
gh-142734: fix OrderedDict copy heap-use-after-free security issue
This commit is contained in:
parent
5989095dfd
commit
2775028ca6
3 changed files with 72 additions and 11 deletions
|
|
@ -10,6 +10,7 @@ import struct
|
|||
import sys
|
||||
import unittest
|
||||
import weakref
|
||||
|
||||
from collections.abc import MutableMapping
|
||||
from test import mapping_tests, support
|
||||
from test.support import import_helper
|
||||
|
|
@ -729,6 +730,32 @@ class OrderedDictTests:
|
|||
with self.assertRaises(ValueError):
|
||||
a |= "BAD"
|
||||
|
||||
def test_getitem_re_entrant_clear_during_copy(self):
|
||||
class Evil(self.OrderedDict):
|
||||
def __getitem__(self, key):
|
||||
super().clear()
|
||||
return None
|
||||
|
||||
evil_dict = Evil([(i, i) for i in range(4)])
|
||||
result = evil_dict.copy()
|
||||
|
||||
self.assertEqual(len(result), 4)
|
||||
|
||||
def test_getitem_re_entrant_modify_during_copy(self):
|
||||
class Modifier(self.OrderedDict):
|
||||
def __getitem__(self, key):
|
||||
self['new_key'] = 'new_value'
|
||||
return super().__getitem__(key)
|
||||
|
||||
original = Modifier([(1, 'one'), (2, 'two')])
|
||||
result = original.copy()
|
||||
|
||||
self.assertIn(1, result)
|
||||
self.assertIn(2, result)
|
||||
self.assertEqual(result[1], 'one')
|
||||
self.assertEqual(result[2], 'two')
|
||||
self.assertEqual(result["new_key"], "new_value")
|
||||
|
||||
@support.cpython_only
|
||||
def test_ordered_dict_items_result_gc(self):
|
||||
# bpo-42536: OrderedDict.items's tuple-reuse speed trick breaks the GC's
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
fix ordereddict copy heap-use-after-free security issue
|
||||
|
|
@ -1266,18 +1266,51 @@ OrderedDict_copy_impl(PyObject *od)
|
|||
}
|
||||
}
|
||||
else {
|
||||
_odict_FOREACH(od, node) {
|
||||
int res;
|
||||
PyObject *value = PyObject_GetItem((PyObject *)od,
|
||||
_odictnode_KEY(node));
|
||||
if (value == NULL)
|
||||
goto fail;
|
||||
res = PyObject_SetItem((PyObject *)od_copy,
|
||||
_odictnode_KEY(node), value);
|
||||
Py_DECREF(value);
|
||||
if (res != 0)
|
||||
goto fail;
|
||||
Py_ssize_t i, size = PyODict_Size((PyODictObject *)od);
|
||||
PyObject **keys;
|
||||
if (size < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return od_copy;
|
||||
}
|
||||
|
||||
keys = PyMem_Malloc(size * sizeof(PyObject *));
|
||||
if (keys == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
_odict_FOREACH(od, node) {
|
||||
keys[i] = _odictnode_KEY(node);
|
||||
Py_INCREF(keys[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
int res;
|
||||
PyObject *value = PyObject_GetItem((PyObject *)od, keys[i]);
|
||||
if (value == NULL) {
|
||||
for (Py_ssize_t j = 0; j < size; j++) {
|
||||
Py_DECREF(keys[j]);
|
||||
}
|
||||
PyMem_Free(keys);
|
||||
goto fail;
|
||||
}
|
||||
res = PyObject_SetItem((PyObject *)od_copy, keys[i], value);
|
||||
Py_DECREF(value);
|
||||
Py_DECREF(keys[i]);
|
||||
if (res != 0) {
|
||||
for (; i < size; i++) {
|
||||
Py_DECREF(keys[i]);
|
||||
}
|
||||
PyMem_Free(keys);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
PyMem_Free(keys);
|
||||
}
|
||||
return od_copy;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue