[Python-checkins] r58032 - in python/trunk: Include/pyerrors.h Lib/test/crashers/infinite_rec_1.py Lib/test/crashers/infinite_rec_2.py Lib/test/crashers/infinite_rec_4.py Lib/test/crashers/infinite_rec_5.py Lib/test/test_descr.py Misc/NEWS Objects/abstract.c Objects/exceptions.c Objects/typeobject.c Python/errors.c

brett.cannon python-checkins at python.org
Fri Sep 7 06:18:30 CEST 2007


Author: brett.cannon
Date: Fri Sep  7 06:18:30 2007
New Revision: 58032

Removed:
   python/trunk/Lib/test/crashers/infinite_rec_1.py
   python/trunk/Lib/test/crashers/infinite_rec_2.py
   python/trunk/Lib/test/crashers/infinite_rec_4.py
   python/trunk/Lib/test/crashers/infinite_rec_5.py
Modified:
   python/trunk/Include/pyerrors.h
   python/trunk/Lib/test/test_descr.py
   python/trunk/Misc/NEWS
   python/trunk/Objects/abstract.c
   python/trunk/Objects/exceptions.c
   python/trunk/Objects/typeobject.c
   python/trunk/Python/errors.c
Log:
Fix a crasher where Python code managed to infinitely recurse in C code without
ever going back out to Python code in PyObject_Call().  Required introducing a
static RuntimeError instance so that normalizing an exception there is no
reliance on a recursive call that would put the exception system over the
recursion check itself.


Modified: python/trunk/Include/pyerrors.h
==============================================================================
--- python/trunk/Include/pyerrors.h	(original)
+++ python/trunk/Include/pyerrors.h	Fri Sep  7 06:18:30 2007
@@ -161,6 +161,7 @@
 #endif
 
 PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst;
+PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
 
 /* Predefined warning categories */
 PyAPI_DATA(PyObject *) PyExc_Warning;

Deleted: /python/trunk/Lib/test/crashers/infinite_rec_1.py
==============================================================================
--- /python/trunk/Lib/test/crashers/infinite_rec_1.py	Fri Sep  7 06:18:30 2007
+++ (empty file)
@@ -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

Deleted: /python/trunk/Lib/test/crashers/infinite_rec_2.py
==============================================================================
--- /python/trunk/Lib/test/crashers/infinite_rec_2.py	Fri Sep  7 06:18:30 2007
+++ (empty file)
@@ -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

Deleted: /python/trunk/Lib/test/crashers/infinite_rec_4.py
==============================================================================
--- /python/trunk/Lib/test/crashers/infinite_rec_4.py	Fri Sep  7 06:18:30 2007
+++ (empty file)
@@ -1,7 +0,0 @@
-
-# http://python.org/sf/1202533
-
-if __name__ == '__main__':
-    lst = [apply]
-    lst.append(lst)
-    apply(*lst)      # segfault: infinite recursion in C

Deleted: /python/trunk/Lib/test/crashers/infinite_rec_5.py
==============================================================================
--- /python/trunk/Lib/test/crashers/infinite_rec_5.py	Fri Sep  7 06:18:30 2007
+++ (empty file)
@@ -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

Modified: python/trunk/Lib/test/test_descr.py
==============================================================================
--- python/trunk/Lib/test/test_descr.py	(original)
+++ python/trunk/Lib/test/test_descr.py	Fri Sep  7 06:18:30 2007
@@ -4,6 +4,7 @@
 from copy import deepcopy
 import warnings
 import types
+import new
 
 warnings.filterwarnings("ignore",
          r'complex divmod\(\), // and % are deprecated$',
@@ -1981,6 +1982,10 @@
     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 @@
             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 @@
         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 @@
     overloading()
     methods()
     specials()
+    recursions()
     weakrefs()
     properties()
     supers()

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Fri Sep  7 06:18:30 2007
@@ -12,6 +12,12 @@
 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.
 

Modified: python/trunk/Objects/abstract.c
==============================================================================
--- python/trunk/Objects/abstract.c	(original)
+++ python/trunk/Objects/abstract.c	Fri Sep  7 06:18:30 2007
@@ -1857,7 +1857,11 @@
         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,

Modified: python/trunk/Objects/exceptions.c
==============================================================================
--- python/trunk/Objects/exceptions.c	(original)
+++ python/trunk/Objects/exceptions.c	Fri Sep  7 06:18:30 2007
@@ -1912,6 +1912,12 @@
  */
 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 @@
     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__)

Modified: python/trunk/Objects/typeobject.c
==============================================================================
--- python/trunk/Objects/typeobject.c	(original)
+++ python/trunk/Objects/typeobject.c	Fri Sep  7 06:18:30 2007
@@ -4854,16 +4854,7 @@
 	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;

Modified: python/trunk/Python/errors.c
==============================================================================
--- python/trunk/Python/errors.c	(original)
+++ python/trunk/Python/errors.c	Fri Sep  7 06:18:30 2007
@@ -132,6 +132,7 @@
 	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 @@
 			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;
 }
 
 


More information about the Python-checkins mailing list