From 4f65331483197a9909b19694688c55fdf9ca7a37 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 20 Mar 2004 22:52:14 +0000 Subject: [PATCH] Limit the nesting depth of a tuple passed as the second argument to isinstance() or issubclass() to the recursion limit of the interpreter. --- Lib/test/test_isinstance.py | 17 ++++++++++++++ Misc/NEWS | 4 ++++ Objects/abstract.c | 45 +++++++++++++++++++++++++++++-------- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index 1b8c593c3cf..45621146248 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -4,6 +4,7 @@ import unittest from test import test_support +import sys @@ -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) def test_main(): diff --git a/Misc/NEWS b/Misc/NEWS index b0693e04445..9f27fd73651 100644 --- 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(). diff --git a/Objects/abstract.c b/Objects/abstract.c index 060abc5cb76..3d6d82920f2 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -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) {