diff options
-rw-r--r-- | Include/pyerrors.h | 1 | ||||
-rw-r--r-- | Lib/test/crashers/infinite_rec_1.py | 11 | ||||
-rw-r--r-- | Lib/test/crashers/infinite_rec_2.py | 10 | ||||
-rw-r--r-- | Lib/test/crashers/infinite_rec_4.py | 7 | ||||
-rw-r--r-- | Lib/test/crashers/infinite_rec_5.py | 10 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 18 | ||||
-rw-r--r-- | Misc/NEWS | 6 | ||||
-rw-r--r-- | Objects/abstract.c | 6 | ||||
-rw-r--r-- | Objects/exceptions.c | 29 | ||||
-rw-r--r-- | Objects/typeobject.c | 9 | ||||
-rw-r--r-- | Python/errors.c | 8 |
11 files changed, 66 insertions, 49 deletions
diff --git a/Include/pyerrors.h b/Include/pyerrors.h index ef7b252..a6ee840 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -161,6 +161,7 @@ PyAPI_DATA(PyObject *) PyExc_VMSError; #endif PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst; +PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst; /* Predefined warning categories */ PyAPI_DATA(PyObject *) PyExc_Warning; diff --git a/Lib/test/crashers/infinite_rec_1.py b/Lib/test/crashers/infinite_rec_1.py deleted file mode 100644 index 573a509..0000000 --- a/Lib/test/crashers/infinite_rec_1.py +++ /dev/null @@ -1,11 +0,0 @@ - -# http://python.org/sf/1202533 - -import new, operator - -class A: - pass -A.__mul__ = new.instancemethod(operator.mul, None, A) - -if __name__ == '__main__': - A()*2 # segfault: infinite recursion in C diff --git a/Lib/test/crashers/infinite_rec_2.py b/Lib/test/crashers/infinite_rec_2.py deleted file mode 100644 index 5a14b33..0000000 --- a/Lib/test/crashers/infinite_rec_2.py +++ /dev/null @@ -1,10 +0,0 @@ - -# http://python.org/sf/1202533 - -class A(str): - __get__ = getattr - -if __name__ == '__main__': - a = A('a') - A.a = a - a.a # segfault: infinite recursion in C diff --git a/Lib/test/crashers/infinite_rec_4.py b/Lib/test/crashers/infinite_rec_4.py deleted file mode 100644 index 14f1520..0000000 --- a/Lib/test/crashers/infinite_rec_4.py +++ /dev/null @@ -1,7 +0,0 @@ - -# http://python.org/sf/1202533 - -if __name__ == '__main__': - lst = [apply] - lst.append(lst) - apply(*lst) # segfault: infinite recursion in C diff --git a/Lib/test/crashers/infinite_rec_5.py b/Lib/test/crashers/infinite_rec_5.py deleted file mode 100644 index 18d2963..0000000 --- a/Lib/test/crashers/infinite_rec_5.py +++ /dev/null @@ -1,10 +0,0 @@ - -# http://python.org/sf/1267884 - -import types - -class C: - __str__ = types.InstanceType.__str__ - -if __name__ == '__main__': - str(C()) # segfault: infinite recursion in C diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 57cef44..9a45cf1 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4,6 +4,7 @@ from test.test_support import verify, vereq, verbose, TestFailed, TESTFN, get_or from copy import deepcopy import warnings import types +import new warnings.filterwarnings("ignore", r'complex divmod\(\), // and % are deprecated$', @@ -1981,6 +1982,10 @@ def specials(): unsafecmp(1, 1L) unsafecmp(1L, 1) +def recursions(): + if verbose: + print "Testing recursion checks ..." + class Letter(str): def __new__(cls, letter): if letter == 'EPS': @@ -1990,7 +1995,6 @@ def specials(): if not self: return 'EPS' return self - # sys.stdout needs to be the original to trigger the recursion bug import sys test_stdout = sys.stdout @@ -2004,6 +2008,17 @@ def specials(): raise TestFailed, "expected a RuntimeError for print recursion" sys.stdout = test_stdout + # Bug #1202533. + class A(object): + pass + A.__mul__ = new.instancemethod(lambda self, x: self * x, None, A) + try: + A()*2 + except RuntimeError: + pass + else: + raise TestFailed("expected a RuntimeError") + def weakrefs(): if verbose: print "Testing weak references..." import weakref @@ -4395,6 +4410,7 @@ def test_main(): overloading() methods() specials() + recursions() weakrefs() properties() supers() @@ -12,6 +12,12 @@ What's New in Python 2.6 alpha 1? Core and builtins ----------------- +- Issue #1202533: Fix infinite recursion calls triggered by calls to + PyObject_Call() never calling back out to Python code to trigger recursion + depth updates/checks. Required the creation of a static RuntimeError + instance in case normalizing an exception put the recursion check value past + its limit. Fixes crashers infinite_rec_(1|2|4|5).py. + - Patch #1031213: Decode source line in SyntaxErrors back to its original source encoding. diff --git a/Objects/abstract.c b/Objects/abstract.c index f7a3bfe..7378e0d 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1857,7 +1857,11 @@ PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) ternaryfunc call; if ((call = func->ob_type->tp_call) != NULL) { - PyObject *result = (*call)(func, arg, kw); + PyObject *result; + if (Py_EnterRecursiveCall(" while calling a Python object")) + return NULL; + result = (*call)(func, arg, kw); + Py_LeaveRecursiveCall(); if (result == NULL && !PyErr_Occurred()) PyErr_SetString( PyExc_SystemError, diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 3d79383..64f655c 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1912,6 +1912,12 @@ SimpleExtendsException(PyExc_Warning, UnicodeWarning, */ PyObject *PyExc_MemoryErrorInst=NULL; +/* Pre-computed RuntimeError instance for when recursion depth is reached. + Meant to be used when normalizing the exception for exceeding the recursion + depth will cause its own infinite recursion. +*/ +PyObject *PyExc_RecursionErrorInst = NULL; + /* module global functions */ static PyMethodDef functions[] = { /* Sentinel */ @@ -2079,6 +2085,29 @@ _PyExc_Init(void) if (!PyExc_MemoryErrorInst) Py_FatalError("Cannot pre-allocate MemoryError instance\n"); + PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL); + if (!PyExc_RecursionErrorInst) + Py_FatalError("Cannot pre-allocate RuntimeError instance for " + "recursion errors"); + else { + PyBaseExceptionObject *err_inst = + (PyBaseExceptionObject *)PyExc_RecursionErrorInst; + PyObject *args_tuple; + PyObject *exc_message; + exc_message = PyString_FromString("maximum recursion depth exceeded"); + if (!exc_message) + Py_FatalError("cannot allocate argument for RuntimeError " + "pre-allocation"); + args_tuple = PyTuple_Pack(1, exc_message); + if (!args_tuple) + Py_FatalError("cannot allocate tuple for RuntimeError " + "pre-allocation"); + Py_DECREF(exc_message); + if (BaseException_init(err_inst, args_tuple, NULL)) + Py_FatalError("init of pre-allocated RuntimeError failed"); + Py_DECREF(args_tuple); + } + Py_DECREF(bltinmod); #if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 49b877d..59dec4a 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4854,16 +4854,7 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds) if (meth == NULL) return NULL; - /* PyObject_Call() will end up calling slot_tp_call() again if - the object returned for __call__ has __call__ itself defined - upon it. This can be an infinite recursion if you set - __call__ in a class to an instance of it. */ - if (Py_EnterRecursiveCall(" in __call__")) { - Py_DECREF(meth); - return NULL; - } res = PyObject_Call(meth, args, kwds); - Py_LeaveRecursiveCall(); Py_DECREF(meth); return res; diff --git a/Python/errors.c b/Python/errors.c index 3b8ea64..63acf33 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -132,6 +132,7 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) PyObject *value = *val; PyObject *inclass = NULL; PyObject *initial_tb = NULL; + PyThreadState *tstate = NULL; if (type == NULL) { /* There was no exception, so nothing to do. */ @@ -207,7 +208,14 @@ 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; + } PyErr_NormalizeException(exc, val, tb); + --tstate->recursion_depth; } |