gh-142734: fix OrderedDict copy heap-use-after-free security issue

This commit is contained in:
fatelei 2025-12-22 15:03:46 +08:00
parent 5989095dfd
commit 2775028ca6
No known key found for this signature in database
GPG key ID: 2F91DA05646F4EED
3 changed files with 72 additions and 11 deletions

View file

@ -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

View file

@ -0,0 +1 @@
fix ordereddict copy heap-use-after-free security issue

View file

@ -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;