Fix a crasher where Python code managed to infinitely recurse in C co… · python/cpython@1e534b5 (original) (raw)

11 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -161,6 +161,7 @@ PyAPI_DATA(PyObject *) PyExc_VMSError;
161 161 #endif
162 162
163 163 PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst;
164 +PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
164 165
165 166 /* Predefined warning categories */
166 167 PyAPI_DATA(PyObject *) PyExc_Warning;
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
4 4 from copy import deepcopy
5 5 import warnings
6 6 import types
7 +import new
7 8
8 9 warnings.filterwarnings("ignore",
9 10 r'complex divmod\(\), // and % are deprecated$',
@@ -1981,6 +1982,10 @@ def unsafecmp(a, b):
1981 1982 unsafecmp(1, 1L)
1982 1983 unsafecmp(1L, 1)
1983 1984
1985 +def recursions():
1986 +if verbose:
1987 +print "Testing recursion checks ..."
1988 +
1984 1989 class Letter(str):
1985 1990 def __new__(cls, letter):
1986 1991 if letter == 'EPS':
@@ -1990,7 +1995,6 @@ def __str__(self):
1990 1995 if not self:
1991 1996 return 'EPS'
1992 1997 return self
1993 -
1994 1998 # sys.stdout needs to be the original to trigger the recursion bug
1995 1999 import sys
1996 2000 test_stdout = sys.stdout
@@ -2004,6 +2008,17 @@ def __str__(self):
2004 2008 raise TestFailed, "expected a RuntimeError for print recursion"
2005 2009 sys.stdout = test_stdout
2006 2010
2011 +# Bug #1202533.
2012 +class A(object):
2013 +pass
2014 +A.__mul__ = new.instancemethod(lambda self, x: self * x, None, A)
2015 +try:
2016 +A()*2
2017 +except RuntimeError:
2018 +pass
2019 +else:
2020 +raise TestFailed("expected a RuntimeError")
2021 +
2007 2022 def weakrefs():
2008 2023 if verbose: print "Testing weak references..."
2009 2024 import weakref
@@ -4395,6 +4410,7 @@ def test_main():
4395 4410 overloading()
4396 4411 methods()
4397 4412 specials()
4413 +recursions()
4398 4414 weakrefs()
4399 4415 properties()
4400 4416 supers()
Original file line number Diff line number Diff line change
@@ -12,6 +12,12 @@ What's New in Python 2.6 alpha 1?
12 12 Core and builtins
13 13 -----------------
14 14
15 +- Issue #1202533: Fix infinite recursion calls triggered by calls to
16 + PyObject_Call() never calling back out to Python code to trigger recursion
17 + depth updates/checks. Required the creation of a static RuntimeError
18 + instance in case normalizing an exception put the recursion check value past
19 + its limit. Fixes crashers infinite_rec_(1|2
20 +
15 21 - Patch #1031213: Decode source line in SyntaxErrors back to its original source
16 22 encoding.
17 23
Original file line number Diff line number Diff line change
@@ -1857,7 +1857,11 @@ PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw)
1857 1857 ternaryfunc call;
1858 1858
1859 1859 if ((call = func->ob_type->tp_call) != NULL) {
1860 -PyObject *result = (*call)(func, arg, kw);
1860 +PyObject *result;
1861 +if (Py_EnterRecursiveCall(" while calling a Python object"))
1862 +return NULL;
1863 +result = (*call)(func, arg, kw);
1864 +Py_LeaveRecursiveCall();
1861 1865 if (result == NULL && !PyErr_Occurred())
1862 1866 PyErr_SetString(
1863 1867 PyExc_SystemError,
Original file line number Diff line number Diff line change
@@ -1912,6 +1912,12 @@ SimpleExtendsException(PyExc_Warning, UnicodeWarning,
1912 1912 */
1913 1913 PyObject *PyExc_MemoryErrorInst=NULL;
1914 1914
1915 +/* Pre-computed RuntimeError instance for when recursion depth is reached.
1916 + Meant to be used when normalizing the exception for exceeding the recursion
1917 + depth will cause its own infinite recursion.
1918 +*/
1919 +PyObject *PyExc_RecursionErrorInst = NULL;
1920 +
1915 1921 /* module global functions */
1916 1922 static PyMethodDef functions[] = {
1917 1923 /* Sentinel */
@@ -2079,6 +2085,29 @@ _PyExc_Init(void)
2079 2085 if (!PyExc_MemoryErrorInst)
2080 2086 Py_FatalError("Cannot pre-allocate MemoryError instance\n");
2081 2087
2088 +PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL);
2089 +if (!PyExc_RecursionErrorInst)
2090 +Py_FatalError("Cannot pre-allocate RuntimeError instance for "
2091 +"recursion errors");
2092 +else {
2093 +PyBaseExceptionObject *err_inst =
2094 + (PyBaseExceptionObject *)PyExc_RecursionErrorInst;
2095 +PyObject *args_tuple;
2096 +PyObject *exc_message;
2097 +exc_message = PyString_FromString("maximum recursion depth exceeded");
2098 +if (!exc_message)
2099 +Py_FatalError("cannot allocate argument for RuntimeError "
2100 +"pre-allocation");
2101 +args_tuple = PyTuple_Pack(1, exc_message);
2102 +if (!args_tuple)
2103 +Py_FatalError("cannot allocate tuple for RuntimeError "
2104 +"pre-allocation");
2105 +Py_DECREF(exc_message);
2106 +if (BaseException_init(err_inst, args_tuple, NULL))
2107 +Py_FatalError("init of pre-allocated RuntimeError failed");
2108 +Py_DECREF(args_tuple);
2109 + }
2110 +
2082 2111 Py_DECREF(bltinmod);
2083 2112
2084 2113 #if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__)
Original file line number Diff line number Diff line change
@@ -4854,16 +4854,7 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
4854 4854 if (meth == NULL)
4855 4855 return NULL;
4856 4856
4857 -/* PyObject_Call() will end up calling slot_tp_call() again if
4858 - the object returned for __call__ has __call__ itself defined
4859 - upon it. This can be an infinite recursion if you set
4860 - __call__ in a class to an instance of it. */
4861 -if (Py_EnterRecursiveCall(" in __call__")) {
4862 -Py_DECREF(meth);
4863 -return NULL;
4864 - }
4865 4857 res = PyObject_Call(meth, args, kwds);
4866 -Py_LeaveRecursiveCall();
4867 4858
4868 4859 Py_DECREF(meth);
4869 4860 return res;
Original file line number Diff line number Diff line change
@@ -132,6 +132,7 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
132 132 PyObject *value = *val;
133 133 PyObject *inclass = NULL;
134 134 PyObject *initial_tb = NULL;
135 +PyThreadState *tstate = NULL;
135 136
136 137 if (type == NULL) {
137 138 /* There was no exception, so nothing to do. */
@@ -207,7 +208,14 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
207 208 Py_DECREF(initial_tb);
208 209 }
209 210 /* normalize recursively */
211 +tstate = PyThreadState_GET();
212 +if (++tstate->recursion_depth > Py_GetRecursionLimit()) {
213 +--tstate->recursion_depth;
214 +PyErr_SetObject(PyExc_RuntimeError, PyExc_RecursionErrorInst);
215 +return;
216 + }
210 217 PyErr_NormalizeException(exc, val, tb);
218 +--tstate->recursion_depth;
211 219 }
212 220
213 221