gh-130555: Fix use-after-free in dict.clear() with embedded values (gh-145268)

This commit is contained in:
Sam Gross 2026-03-02 12:25:13 -05:00 committed by GitHub
parent 107863ee17
commit 02288bf022
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 88 additions and 9 deletions

View file

@ -2969,6 +2969,21 @@ _PyDict_DelItemIf(PyObject *op, PyObject *key,
return res;
}
static void
clear_embedded_values(PyDictValues *values, Py_ssize_t nentries)
{
PyObject *refs[SHARED_KEYS_MAX_SIZE];
assert(nentries <= SHARED_KEYS_MAX_SIZE);
for (Py_ssize_t i = 0; i < nentries; i++) {
refs[i] = values->values[i];
values->values[i] = NULL;
}
values->size = 0;
for (Py_ssize_t i = 0; i < nentries; i++) {
Py_XDECREF(refs[i]);
}
}
static void
clear_lock_held(PyObject *op)
{
@ -2997,20 +3012,18 @@ clear_lock_held(PyObject *op)
assert(oldkeys->dk_refcnt == 1);
dictkeys_decref(oldkeys, IS_DICT_SHARED(mp));
}
else if (oldvalues->embedded) {
clear_embedded_values(oldvalues, oldkeys->dk_nentries);
}
else {
set_values(mp, NULL);
set_keys(mp, Py_EMPTY_KEYS);
n = oldkeys->dk_nentries;
for (i = 0; i < n; i++) {
Py_CLEAR(oldvalues->values[i]);
}
if (oldvalues->embedded) {
oldvalues->size = 0;
}
else {
set_values(mp, NULL);
set_keys(mp, Py_EMPTY_KEYS);
free_values(oldvalues, IS_DICT_SHARED(mp));
dictkeys_decref(oldkeys, false);
}
free_values(oldvalues, IS_DICT_SHARED(mp));
dictkeys_decref(oldkeys, false);
}
ASSERT_CONSISTENT(mp);
}