https://github.com/python/cpython/commit/d177460b4317b3ef3fbbd795de63e39d83569a9b
commit: d177460b4317b3ef3fbbd795de63e39d83569a9b
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: encukou <[email protected]>
date: 2026-02-10T14:01:23+01:00
summary:

[3.13] gh-143543: Fix re-entrant use-after-free in itertools.groupby 
(GH-143738) (GH-144627)

(cherry picked from commit a91b5c3fb5aeaeda6a8e016378beb0e4a8b329e6)

Co-authored-by: VanshAgarwal24036 
<[email protected]>
Co-authored-by: Sergey B Kirpichev <[email protected]>
Co-authored-by: Petr Viktorin <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-01-13-10-38-43.gh-issue-143543.DeQRCO.rst
M Lib/test/test_itertools.py
M Modules/itertoolsmodule.c

diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 7d5ba7273890ae..093aa48d48a4bb 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -997,6 +997,27 @@ def keyfunc(obj):
         keyfunc.skip = 1
         self.assertRaises(ExpectedError, gulp, [None, None], keyfunc)
 
+    def test_groupby_reentrant_eq_does_not_crash(self):
+        # regression test for gh-143543
+        class Key:
+            def __init__(self, do_advance):
+                self.do_advance = do_advance
+
+            def __eq__(self, other):
+                if self.do_advance:
+                    self.do_advance = False
+                    next(g)
+                    return NotImplemented
+                return False
+
+        def keys():
+            yield Key(True)
+            yield Key(False)
+
+        g = itertools.groupby([None, None], keys().send)
+        next(g)
+        next(g)  # must pass with address sanitizer
+
     def test_filter(self):
         self.assertEqual(list(filter(isEven, range(6))), [0,2,4])
         self.assertEqual(list(filter(None, [0,1,0,2,0])), [1,2])
diff --git 
a/Misc/NEWS.d/next/Library/2026-01-13-10-38-43.gh-issue-143543.DeQRCO.rst 
b/Misc/NEWS.d/next/Library/2026-01-13-10-38-43.gh-issue-143543.DeQRCO.rst
new file mode 100644
index 00000000000000..14622a395ec22e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-01-13-10-38-43.gh-issue-143543.DeQRCO.rst
@@ -0,0 +1,2 @@
+Fix a crash in itertools.groupby that could occur when a user-defined
+:meth:`~object.__eq__` method re-enters the iterator during key comparison.
diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c
index 8ed7683be28e6c..d967904069e988 100644
--- a/Modules/itertoolsmodule.c
+++ b/Modules/itertoolsmodule.c
@@ -531,9 +531,19 @@ groupby_next(groupbyobject *gbo)
         else if (gbo->tgtkey == NULL)
             break;
         else {
-            int rcmp;
+            /* A user-defined __eq__ can re-enter groupby and advance the 
iterator,
+               mutating gbo->tgtkey / gbo->currkey while we are comparing them.
+               Take local snapshots and hold strong references so INCREF/DECREF
+               apply to the same objects even under re-entrancy. */
+            PyObject *tgtkey = gbo->tgtkey;
+            PyObject *currkey = gbo->currkey;
+
+            Py_INCREF(tgtkey);
+            Py_INCREF(currkey);
+            int rcmp = PyObject_RichCompareBool(tgtkey, currkey, Py_EQ);
+            Py_DECREF(tgtkey);
+            Py_DECREF(currkey);
 
-            rcmp = PyObject_RichCompareBool(gbo->tgtkey, gbo->currkey, Py_EQ);
             if (rcmp == -1)
                 return NULL;
             else if (rcmp == 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