diff -r 99bfab5feae5 Include/ceval.h --- a/Include/ceval.h Fri Aug 29 23:21:52 2008 +0200 +++ b/Include/ceval.h Sat Aug 30 02:12:40 2008 +0200 @@ -53,7 +53,17 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(voi (_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \ _Py_CheckRecursiveCall(where)) #define Py_LeaveRecursiveCall() \ - (--PyThreadState_GET()->recursion_depth) + do{ if((--PyThreadState_GET()->recursion_depth) < \ + _Py_CheckRecursionLimit - 50) \ + PyThreadState_GET()->overflowed = 0; \ + } while(0) + +#define Py_ClearRecursionOverflow() \ + do{ if(PyThreadState_GET()->recursion_depth <= \ + _Py_CheckRecursionLimit) \ + PyThreadState_GET()->overflowed = 0; \ + } while(0) + PyAPI_FUNC(int) _Py_CheckRecursiveCall(char *where); PyAPI_DATA(int) _Py_CheckRecursionLimit; #ifdef USE_STACKCHECK diff -r 99bfab5feae5 Include/pystate.h --- a/Include/pystate.h Fri Aug 29 23:21:52 2008 +0200 +++ b/Include/pystate.h Sat Aug 30 02:12:40 2008 +0200 @@ -61,6 +61,8 @@ typedef struct _ts { struct _frame *frame; int recursion_depth; + char overflowed; /* The stack has overflowed. Allow 50 more calls + to handle the runtime error. */ /* 'tracing' keeps track of the execution depth when tracing/profiling. This is to prevent the actual trace/profile code from being recorded in the trace/profile. */ diff -r 99bfab5feae5 Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py Fri Aug 29 23:21:52 2008 +0200 +++ b/Lib/test/test_exceptions.py Sat Aug 30 02:12:40 2008 +0200 @@ -4,6 +4,9 @@ import sys import sys import unittest import pickle, cPickle +import types +import new +import operator from test.test_support import (TESTFN, unlink, run_unittest, catch_warning, captured_output) @@ -323,30 +326,6 @@ class ExceptionTests(unittest.TestCase): x = DerivedException(fancy_arg=42) self.assertEquals(x.fancy_arg, 42) - def testInfiniteRecursion(self): - def f(): - return f() - self.assertRaises(RuntimeError, f) - - def g(): - try: - return g() - except ValueError: - return -1 - - # The test prints an unraisable recursion error when - # doing "except ValueError", this is because subclass - # checking has recursion checking too. - with captured_output("stderr"): - try: - g() - except RuntimeError: - pass - except: - self.fail("Should have raised KeyError") - else: - self.fail("Should have raised KeyError") - def testUnicodeStrUsage(self): # Make sure both instances and classes have a str and unicode # representation. @@ -379,7 +358,35 @@ class ExceptionTests(unittest.TestCase): else: self.fail("Should have raised KeyError") - with captured_output("stderr") as stderr: + +class InfiniteRecursionTests(unittest.TestCase): + + def test_recurse(self): + def f(): + return f() + self.assertRaises(RuntimeError, f) + + def test_recurse_and_catch(self): + def g(): + try: + return g() + except ValueError: + return -1 + + # The test prints an unraisable recursion error when + # doing "except ValueError", this is because subclass + # checking has recursion checking too. + with captured_output("stderr"): + try: + g() + except RuntimeError: + pass + except: + self.fail("Should have raised RuntimeError") + else: + self.fail("Should have raised RuntimeError") + + with captured_output("stderr"): def g(): try: return g() @@ -389,9 +396,46 @@ class ExceptionTests(unittest.TestCase): self.assert_(e is RuntimeError, e) self.assert_("maximum recursion depth exceeded" in str(v), v) + # Following crashers are from http://python.org/sf/1202533 + + def test_crasher1(self): + class A: + pass + A.__mul__ = new.instancemethod(operator.mul, None, A) + try: + A()*2 # segfault: infinite recursion in C + except (AttributeError, RuntimeError): + pass + + def test_crasher2(self): + class A(str): + __get__ = getattr + try: + a = A('a') + A.a = a + a.a # segfault: infinite recursion in C + except (AttributeError, RuntimeError): + pass + + def test_crasher3(self): + lst = [apply] + lst.append(lst) + try: + apply(*lst) # segfault: infinite recursion in C + except (TypeError, RuntimeError): + pass + + def test_crasher4(self): + class C: + __str__ = types.InstanceType.__str__ + try: + str(C()) # segfault: infinite recursion in C + except (AttributeError, RuntimeError): + pass + def test_main(): - run_unittest(ExceptionTests) + run_unittest(ExceptionTests, InfiniteRecursionTests) if __name__ == '__main__': test_main() diff -r 99bfab5feae5 Modules/cPickle.c --- a/Modules/cPickle.c Fri Aug 29 23:21:52 2008 +0200 +++ b/Modules/cPickle.c Sat Aug 30 02:12:40 2008 +0200 @@ -864,6 +864,8 @@ whichmodule(PyObject *global, PyObject * while ((j = PyDict_Next(modules_dict, &i, &name, &module))) { if (PyObject_Compare(name, __main___str)==0) continue; + if (PyErr_Occurred()) + return NULL; global_name_attr = PyObject_GetAttr(module, global_name); if (!global_name_attr) { diff -r 99bfab5feae5 Python/ceval.c --- a/Python/ceval.c Fri Aug 29 23:21:52 2008 +0200 +++ b/Python/ceval.c Sat Aug 30 02:12:40 2008 +0200 @@ -479,8 +479,16 @@ _Py_CheckRecursiveCall(char *where) return -1; } #endif + if (tstate->overflowed) { + if (tstate->recursion_depth > recursion_limit + 50) { + /* Overflowing while handling an overflow. Give up. */ + Py_FatalError("Cannot recover from stack overflow."); + } + return 0; + } if (tstate->recursion_depth > recursion_limit) { --tstate->recursion_depth; + tstate->overflowed = 1; PyErr_Format(PyExc_RuntimeError, "maximum recursion depth exceeded%s", where); diff -r 99bfab5feae5 Python/errors.c --- a/Python/errors.c Fri Aug 29 23:21:52 2008 +0200 +++ b/Python/errors.c Sat Aug 30 02:12:40 2008 +0200 @@ -140,7 +140,6 @@ PyErr_NormalizeException(PyObject **exc, PyObject *value = *val; PyObject *inclass = NULL; PyObject *initial_tb = NULL; - PyThreadState *tstate = NULL; if (type == NULL) { /* There was no exception, so nothing to do. */ @@ -216,14 +215,11 @@ finally: Py_DECREF(initial_tb); } /* normalize recursively */ - tstate = PyThreadState_GET(); - if (++tstate->recursion_depth > Py_GetRecursionLimit()) { - --tstate->recursion_depth; - PyErr_SetObject(PyExc_RuntimeError, PyExc_RecursionErrorInst); - return; + if (Py_EnterRecursiveCall(" while normalizing exception")) { + PyErr_Fetch(exc, val, tb); } PyErr_NormalizeException(exc, val, tb); - --tstate->recursion_depth; + Py_LeaveRecursiveCall(); } @@ -244,6 +240,10 @@ void void PyErr_Clear(void) { + /* Assume we are clearing traces of a potential recursion error, and + therefore clear the overflow flag as well if possible (i.e. if we + are really below the recursion limit). */ + Py_ClearRecursionOverflow(); PyErr_Restore(NULL, NULL, NULL); } diff -r 99bfab5feae5 Python/pystate.c --- a/Python/pystate.c Fri Aug 29 23:21:52 2008 +0200 +++ b/Python/pystate.c Sat Aug 30 02:12:40 2008 +0200 @@ -167,6 +167,7 @@ PyThreadState_New(PyInterpreterState *in tstate->frame = NULL; tstate->recursion_depth = 0; + tstate->overflowed = 0; tstate->tracing = 0; tstate->use_tracing = 0; tstate->tick_counter = 0;