]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Raise RuntimeError if the second argument to isinstance() or issubclass()
authorBrett Cannon <bcannon@gmail.com>
Sat, 20 Mar 2004 21:54:35 +0000 (21:54 +0000)
committerBrett Cannon <bcannon@gmail.com>
Sat, 20 Mar 2004 21:54:35 +0000 (21:54 +0000)
is a tuple nested to a depth beyond the interpreter's recursion limit to
prevent a segfault from blowing the C stack.
Fixes bug #858016 .

Lib/test/test_isinstance.py
Misc/NEWS
Objects/abstract.c

index 1b8c593c3cff6b28b82f96ec1e7d90cbc0e5b932..7e297f08e13b87bb71e8d8527a8a6a0cef32e557 100644 (file)
@@ -4,6 +4,7 @@
 
 import unittest
 from test import test_support
+import sys
 
 
 \f
@@ -244,6 +245,24 @@ class TestIsInstanceIsSubclass(unittest.TestCase):
         self.assertEqual(True, issubclass(int, (long, (float, int))))
         self.assertEqual(True, issubclass(str, (unicode, (Child, NewChild, basestring))))
 
+    def test_subclass_recursion_limit(self):
+        # make sure that issubclass raises RuntimeError before the C stack is
+        # blown
+        self.assertRaises(RuntimeError, blowstack, issubclass, str, str)
+
+    def test_isinstance_recursion_limit(self):
+        # make sure that issubclass raises RuntimeError before the C stack is
+        # blown
+        self.assertRaises(RuntimeError, blowstack, isinstance, '', str)
+
+def blowstack(fxn, arg, compare_to):
+    # Make sure that calling isinstance with a deeply nested tuple for its
+    # argument will raise RuntimeError eventually.
+    tuple_arg = (compare_to,)
+    for cnt in xrange(sys.getrecursionlimit()+5):
+        tuple_arg = (tuple_arg,)
+        fxn(arg, tuple_arg)
+
 
 
 \f
index 15599430caf28fb0fff3449225e463686c32e21b..e40e66c7e80697870283b220e950fbdb363a6461 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -34,7 +34,11 @@ Core and builtins
 Library
 -------
 
-- bug 700055: .pth files can now have any type of line endings.
+- Bug #858016: isinstance() and issubclass() can have their second
+  argument be a tuple whose nested depth is capped at the interpreter's
+  recursion limit.  Raises RuntimeError if the limit reached.
+
+- Bug #700055: .pth files can now have any type of line endings.
 
 - Patch 817379: Allow absolute ftp paths in urllib2.
 
index 36c1608767a5a8d26b22e8ba1aa3fca4766136c0..13185d8fd7f64b17fe4f9c9f78eaaff88510c001 100644 (file)
@@ -1997,6 +1997,8 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
        if (PyTuple_Check(cls)) {
                /* Not a general sequence -- that opens up the road to
                   recursion and stack overflow. */
+                /* XXX: really an issue even though no subsequences get
+                        iterated over? */
                n = PyTuple_GET_SIZE(cls);
                for (i = 0; i < n; i++) {
                        if (derived == PyTuple_GET_ITEM(cls, i))
@@ -2035,8 +2037,8 @@ check_class(PyObject *cls, const char *error)
        return -1;
 }
 
-int
-PyObject_IsInstance(PyObject *inst, PyObject *cls)
+static int
+recursive_isinstance(PyObject *inst, PyObject *cls, int recursion_depth)
 {
        PyObject *icls;
        static PyObject *__class__ = NULL;
@@ -2071,14 +2073,20 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls)
                }
        }
        else if (PyTuple_Check(cls)) {
-               /* Not a general sequence -- that opens up the road to
-                  recursion and stack overflow. */
                int i, n;
 
+                if (!recursion_depth) {
+                    PyErr_SetString(PyExc_RuntimeError,
+                                    "Recursion depth exceeded");
+                    return NULL;
+                }
+
                n = PyTuple_GET_SIZE(cls);
                for (i = 0; i < n; i++) {
-                       retval = PyObject_IsInstance(
-                               inst, PyTuple_GET_ITEM(cls, i));
+                       retval = recursive_isinstance(
+                                    inst,
+                                    PyTuple_GET_ITEM(cls, i),
+                                    recursion_depth-1);
                        if (retval != 0)
                                break;
                }
@@ -2102,8 +2110,19 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls)
        return retval;
 }
 
+/* Use recursive_isinstance to have a hard limit on the depth of the possible
+    tuple second argument.
+    
+   Done to prevent segfaulting by blowing the C stack.
+*/
 int
-PyObject_IsSubclass(PyObject *derived, PyObject *cls)
+PyObject_IsInstance(PyObject *inst, PyObject *cls)
+{
+    return recursive_isinstance(inst, cls, Py_GetRecursionLimit());
+}
+
+static int
+recursive_issubclass(PyObject *derived, PyObject *cls, int recursion_depth)
 {
        int retval;
 
@@ -2115,9 +2134,18 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls)
                if (PyTuple_Check(cls)) {
                        int i;
                        int n = PyTuple_GET_SIZE(cls);
+
+                        if (!recursion_depth) {
+                            PyErr_SetString(PyExc_RuntimeError,
+                                            "Recursion depth exceeded");
+                            return NULL;
+                        }
+                        
                        for (i = 0; i < n; ++i) {
-                               retval = PyObject_IsSubclass(
-                                       derived, PyTuple_GET_ITEM(cls, i));
+                               retval = recursive_issubclass(
+                                            derived,
+                                            PyTuple_GET_ITEM(cls, i),
+                                            recursion_depth-1);
                                if (retval != 0) {
                                        /* either found it, or got an error */
                                        return retval;
@@ -2143,6 +2171,17 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls)
        return retval;
 }
 
+/* Use recursive_issubclass to have a hard limit on the depth of the possible
+    tuple second argument.
+    
+   Done to prevent segfaulting by blowing the C stack.
+*/
+int
+PyObject_IsSubclass(PyObject *derived, PyObject *cls)
+{
+    return recursive_issubclass(derived, cls, Py_GetRecursionLimit());
+}
+
 PyObject *
 PyObject_GetIter(PyObject *o)
 {