]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-128049: Fix type confusion bug with the return value of a custom ExceptionG...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 20 Dec 2024 20:19:12 +0000 (21:19 +0100)
committerGitHub <noreply@github.com>
Fri, 20 Dec 2024 20:19:12 +0000 (20:19 +0000)
gh-128049: Fix type confusion bug with the return value of a custom ExceptionGroup split function (GH-128079)
(cherry picked from commit 3879ca0100942ae15a09ac22889cbe3e46d424eb)

Co-authored-by: Nico-Posada <102486290+Nico-Posada@users.noreply.github.com>
Lib/test/test_except_star.py
Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst [new file with mode: 0644]
Python/ceval.c

index c49c6008e08e8ce9e0731b90d25391c5aa34f777..284907f61213f8abe66be078f4e81fa730779618 100644 (file)
@@ -952,6 +952,49 @@ class TestExceptStarExceptionGroupSubclass(ExceptStarTest):
         self.assertExceptionIsLike(tes, FalsyEG("eg", [TypeError(1)]))
         self.assertExceptionIsLike(ves, FalsyEG("eg", [ValueError(2)]))
 
+    def test_exception_group_subclass_with_bad_split_func(self):
+        # see gh-128049.
+        class BadEG1(ExceptionGroup):
+            def split(self, *args):
+                return "NOT A 2-TUPLE!"
+
+        class BadEG2(ExceptionGroup):
+            def split(self, *args):
+                return ("NOT A 2-TUPLE!",)
+
+        eg_list = [
+            (BadEG1("eg", [OSError(123), ValueError(456)]),
+             r"split must return a tuple, not str"),
+            (BadEG2("eg", [OSError(123), ValueError(456)]),
+             r"split must return a 2-tuple, got tuple of size 1")
+        ]
+
+        for eg_class, msg in eg_list:
+            with self.assertRaisesRegex(TypeError, msg) as m:
+                try:
+                    raise eg_class
+                except* ValueError:
+                    pass
+                except* OSError:
+                    pass
+
+            self.assertExceptionIsLike(m.exception.__context__, eg_class)
+
+        # we allow tuples of length > 2 for backwards compatibility
+        class WeirdEG(ExceptionGroup):
+            def split(self, *args):
+                return super().split(*args) + ("anything", 123456, None)
+
+        try:
+            raise WeirdEG("eg", [OSError(123), ValueError(456)])
+        except* OSError as e:
+            oeg = e
+        except* ValueError as e:
+            veg = e
+
+        self.assertExceptionIsLike(oeg, WeirdEG("eg", [OSError(123)]))
+        self.assertExceptionIsLike(veg, WeirdEG("eg", [ValueError(456)]))
+
 
 class TestExceptStarCleanup(ExceptStarTest):
     def test_sys_exception_restored(self):
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-18-14-22-48.gh-issue-128079.SUD5le.rst
new file mode 100644 (file)
index 0000000..8da4e67
--- /dev/null
@@ -0,0 +1,5 @@
+Fix a bug where :keyword:`except* <except_star>` does not properly check the
+return value of an :exc:`ExceptionGroup`'s :meth:`~BaseExceptionGroup.split`
+function, leading to a crash in some cases. Now when :meth:`~BaseExceptionGroup.split`
+returns an invalid object, :keyword:`except* <except_star>` raises a :exc:`TypeError`
+with the original raised :exc:`ExceptionGroup` object chained to it.
index 6110883ca0e7f29d60281bb73b860e518d9d4479..b979294b20ff6bded58762929d2eef1347db0f27 100644 (file)
@@ -1898,8 +1898,25 @@ exception_group_match(PyObject* exc_value, PyObject *match_type,
         if (pair == NULL) {
             return -1;
         }
-        assert(PyTuple_CheckExact(pair));
-        assert(PyTuple_GET_SIZE(pair) == 2);
+
+        if (!PyTuple_CheckExact(pair)) {
+            PyErr_Format(PyExc_TypeError,
+                         "%.200s.split must return a tuple, not %.200s",
+                         Py_TYPE(exc_value)->tp_name, Py_TYPE(pair)->tp_name);
+            Py_DECREF(pair);
+            return -1;
+        }
+
+        // allow tuples of length > 2 for backwards compatibility
+        if (PyTuple_GET_SIZE(pair) < 2) {
+            PyErr_Format(PyExc_TypeError,
+                         "%.200s.split must return a 2-tuple, "
+                         "got tuple of size %zd",
+                         Py_TYPE(exc_value)->tp_name, PyTuple_GET_SIZE(pair));
+            Py_DECREF(pair);
+            return -1;
+        }
+
         *match = Py_NewRef(PyTuple_GET_ITEM(pair, 0));
         *rest = Py_NewRef(PyTuple_GET_ITEM(pair, 1));
         Py_DECREF(pair);