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 |