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

[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 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 843f4ffcd25aa4..4b9b47cd740a57 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -2119,6 +2119,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 b356c917c1d5ef..f5fc0d02a16607 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -2549,7 +2549,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