]> granicus.if.org Git - python/commitdiff
Limit the nesting depth of a tuple passed as the second argument to
authorBrett Cannon <bcannon@gmail.com>
Sat, 20 Mar 2004 22:52:14 +0000 (22:52 +0000)
committerBrett Cannon <bcannon@gmail.com>
Sat, 20 Mar 2004 22:52:14 +0000 (22:52 +0000)
isinstance() or issubclass() to the recursion limit of the interpreter.

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

index 1b8c593c3cff6b28b82f96ec1e7d90cbc0e5b932..4562114624863c76e4b56cd1f70b07b787407ef4 100644 (file)
@@ -4,6 +4,7 @@
 
 import unittest
 from test import test_support
+import sys
 
 
 \f
@@ -244,7 +245,23 @@ 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
 def test_main():
index b0693e04445f84a7479471a24a9f3e06ef40f24a..9f27fd73651447e05c598209ff3724d8e184b18a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,10 @@ What's New in Python 2.4 alpha 1?
 Core and builtins
 -----------------
 
+- Limit the nested depth of a tuple for the second argument to isinstance()
+  and issubclass() to the recursion limit of the interpreter.
+  Fixes bug  #858016 .
+
 - Optimized dict iterators, creating separate types for each
   and having them reveal their length.  Also optimized the
   methods:  keys(), values(), and items().
index 060abc5cb7683f9eb1f5d127afbec08c78bb0049..3d6d82920f2c3a582c2247f06837e4614c001fd9 100644 (file)
@@ -1992,8 +1992,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;
@@ -2028,14 +2028,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,
+                                    "nest level of tuple too deep");
+                    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;
                }
@@ -2060,7 +2066,13 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls)
 }
 
 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;
 
@@ -2072,9 +2084,17 @@ 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,
+                                            "nest level of tuple too deep");
+                            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;
@@ -2100,6 +2120,13 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls)
        return retval;
 }
 
+int
+PyObject_IsSubclass(PyObject *derived, PyObject *cls)
+{
+    return recursive_issubclass(derived, cls, Py_GetRecursionLimit());
+}
+
+
 PyObject *
 PyObject_GetIter(PyObject *o)
 {