https://github.com/python/cpython/commit/240ee207708e4103e4a9f459ff9bf285511e43cc
commit: 240ee207708e4103e4a9f459ff9bf285511e43cc
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-12-25T13:41:10+02:00
summary:

[3.14] gh-143004: Fix possible use-after-free in collections.Counter.update() 
(GH-143044) (GH-143166)

This happened when the Counter was mutated when incrementing
the value for an existing key.
(cherry picked from commit 86d904588e8c84c7fccb8faf84b343f03461970d)

Co-authored-by: kaushal trivedi 
<[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-12-22-00-00-00.gh-issue-143004.uaf-counter.rst
M Lib/test/test_collections.py
M Modules/_collectionsmodule.c

diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 3dac736e0189b1..c1dadc4e274ec9 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -2118,6 +2118,19 @@ def test_basics(self):
         self.assertEqual(c.setdefault('e', 5), 5)
         self.assertEqual(c['e'], 5)
 
+    def test_update_reentrant_add_clears_counter(self):
+        c = Counter()
+        key = object()
+
+        class Evil(int):
+            def __add__(self, other):
+                c.clear()
+                return NotImplemented
+
+        c[key] = Evil()
+        c.update([key])
+        self.assertEqual(c[key], 1)
+
     def test_init(self):
         self.assertEqual(list(Counter(self=42).items()), [('self', 42)])
         self.assertEqual(list(Counter(iterable=42).items()), [('iterable', 
42)])
diff --git 
a/Misc/NEWS.d/next/Library/2025-12-22-00-00-00.gh-issue-143004.uaf-counter.rst 
b/Misc/NEWS.d/next/Library/2025-12-22-00-00-00.gh-issue-143004.uaf-counter.rst
new file mode 100644
index 00000000000000..278066e9b706bf
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Library/2025-12-22-00-00-00.gh-issue-143004.uaf-counter.rst
@@ -0,0 +1,2 @@
+Fix a potential use-after-free in :meth:`collections.Counter.update` when user 
code
+mutates the Counter during an update.
diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c
index 3b14a21fa8428e..45ca63e6d7c77f 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -2577,7 +2577,12 @@ _collections__count_elements_impl(PyObject *module, 
PyObject *mapping,
                 if (_PyDict_SetItem_KnownHash(mapping, key, one, hash) < 0)
                     goto done;
             } else {
+                /* oldval is a borrowed reference.  Keep it alive across
+                   PyNumber_Add(), which can execute arbitrary user code and
+                   mutate (or even clear) the underlying dict. */
+                Py_INCREF(oldval);
                 newval = PyNumber_Add(oldval, one);
+                Py_DECREF(oldval);
                 if (newval == NULL)
                     goto done;
                 if (_PyDict_SetItem_KnownHash(mapping, key, newval, hash) < 0)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to