Skip to content

Commit 4dd9d94

Browse files
[3.13] gh-143004: Fix possible use-after-free in collections.Counter.update() (GH-143044) (GH-143167)
This happened when the Counter was mutated when incrementing the value for an existing key. (cherry picked from commit 86d9045) Co-authored-by: kaushal trivedi <155625932+Kaushalt2004@users.noreply.github.com>
1 parent e132893 commit 4dd9d94

File tree

3 files changed

+20
-0
lines changed

3 files changed

+20
-0
lines changed

Lib/test/test_collections.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,19 @@ def test_basics(self):
21192119
self.assertEqual(c.setdefault('e', 5), 5)
21202120
self.assertEqual(c['e'], 5)
21212121

2122+
def test_update_reentrant_add_clears_counter(self):
2123+
c = Counter()
2124+
key = object()
2125+
2126+
class Evil(int):
2127+
def __add__(self, other):
2128+
c.clear()
2129+
return NotImplemented
2130+
2131+
c[key] = Evil()
2132+
c.update([key])
2133+
self.assertEqual(c[key], 1)
2134+
21222135
def test_init(self):
21232136
self.assertEqual(list(Counter(self=42).items()), [('self', 42)])
21242137
self.assertEqual(list(Counter(iterable=42).items()), [('iterable', 42)])
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a potential use-after-free in :meth:`collections.Counter.update` when user code
2+
mutates the Counter during an update.

Modules/_collectionsmodule.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,7 +2549,12 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping,
25492549
if (_PyDict_SetItem_KnownHash(mapping, key, one, hash) < 0)
25502550
goto done;
25512551
} else {
2552+
/* oldval is a borrowed reference. Keep it alive across
2553+
PyNumber_Add(), which can execute arbitrary user code and
2554+
mutate (or even clear) the underlying dict. */
2555+
Py_INCREF(oldval);
25522556
newval = PyNumber_Add(oldval, one);
2557+
Py_DECREF(oldval);
25532558
if (newval == NULL)
25542559
goto done;
25552560
if (_PyDict_SetItem_KnownHash(mapping, key, newval, hash) < 0)

0 commit comments

Comments
 (0)