[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