mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
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.
This commit is contained in:
parent
68a6da99e6
commit
1e534b5425
11 changed files with 66 additions and 49 deletions
|
@ -161,6 +161,7 @@ PyAPI_DATA(PyObject *) PyExc_VMSError;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst;
|
PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst;
|
||||||
|
PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
|
||||||
|
|
||||||
/* Predefined warning categories */
|
/* Predefined warning categories */
|
||||||
PyAPI_DATA(PyObject *) PyExc_Warning;
|
PyAPI_DATA(PyObject *) PyExc_Warning;
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1,7 +0,0 @@
|
||||||
|
|
||||||
# http://python.org/sf/1202533
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
lst = [apply]
|
|
||||||
lst.append(lst)
|
|
||||||
apply(*lst) # segfault: infinite recursion in C
|
|
|
@ -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
|
|
|
@ -4,6 +4,7 @@ from test.test_support import verify, vereq, verbose, TestFailed, TESTFN, get_or
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import warnings
|
import warnings
|
||||||
import types
|
import types
|
||||||
|
import new
|
||||||
|
|
||||||
warnings.filterwarnings("ignore",
|
warnings.filterwarnings("ignore",
|
||||||
r'complex divmod\(\), // and % are deprecated$',
|
r'complex divmod\(\), // and % are deprecated$',
|
||||||
|
@ -1981,6 +1982,10 @@ def specials():
|
||||||
unsafecmp(1, 1L)
|
unsafecmp(1, 1L)
|
||||||
unsafecmp(1L, 1)
|
unsafecmp(1L, 1)
|
||||||
|
|
||||||
|
def recursions():
|
||||||
|
if verbose:
|
||||||
|
print "Testing recursion checks ..."
|
||||||
|
|
||||||
class Letter(str):
|
class Letter(str):
|
||||||
def __new__(cls, letter):
|
def __new__(cls, letter):
|
||||||
if letter == 'EPS':
|
if letter == 'EPS':
|
||||||
|
@ -1990,7 +1995,6 @@ def specials():
|
||||||
if not self:
|
if not self:
|
||||||
return 'EPS'
|
return 'EPS'
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# sys.stdout needs to be the original to trigger the recursion bug
|
# sys.stdout needs to be the original to trigger the recursion bug
|
||||||
import sys
|
import sys
|
||||||
test_stdout = sys.stdout
|
test_stdout = sys.stdout
|
||||||
|
@ -2004,6 +2008,17 @@ def specials():
|
||||||
raise TestFailed, "expected a RuntimeError for print recursion"
|
raise TestFailed, "expected a RuntimeError for print recursion"
|
||||||
sys.stdout = test_stdout
|
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():
|
def weakrefs():
|
||||||
if verbose: print "Testing weak references..."
|
if verbose: print "Testing weak references..."
|
||||||
import weakref
|
import weakref
|
||||||
|
@ -4395,6 +4410,7 @@ def test_main():
|
||||||
overloading()
|
overloading()
|
||||||
methods()
|
methods()
|
||||||
specials()
|
specials()
|
||||||
|
recursions()
|
||||||
weakrefs()
|
weakrefs()
|
||||||
properties()
|
properties()
|
||||||
supers()
|
supers()
|
||||||
|
|
|
@ -12,6 +12,12 @@ What's New in Python 2.6 alpha 1?
|
||||||
Core and builtins
|
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
|
- Patch #1031213: Decode source line in SyntaxErrors back to its original source
|
||||||
encoding.
|
encoding.
|
||||||
|
|
||||||
|
|
|
@ -1857,7 +1857,11 @@ PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw)
|
||||||
ternaryfunc call;
|
ternaryfunc call;
|
||||||
|
|
||||||
if ((call = func->ob_type->tp_call) != NULL) {
|
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())
|
if (result == NULL && !PyErr_Occurred())
|
||||||
PyErr_SetString(
|
PyErr_SetString(
|
||||||
PyExc_SystemError,
|
PyExc_SystemError,
|
||||||
|
|
|
@ -1912,6 +1912,12 @@ SimpleExtendsException(PyExc_Warning, UnicodeWarning,
|
||||||
*/
|
*/
|
||||||
PyObject *PyExc_MemoryErrorInst=NULL;
|
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 */
|
/* module global functions */
|
||||||
static PyMethodDef functions[] = {
|
static PyMethodDef functions[] = {
|
||||||
/* Sentinel */
|
/* Sentinel */
|
||||||
|
@ -2079,6 +2085,29 @@ _PyExc_Init(void)
|
||||||
if (!PyExc_MemoryErrorInst)
|
if (!PyExc_MemoryErrorInst)
|
||||||
Py_FatalError("Cannot pre-allocate MemoryError instance\n");
|
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);
|
Py_DECREF(bltinmod);
|
||||||
|
|
||||||
#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__)
|
#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__)
|
||||||
|
|
|
@ -4854,16 +4854,7 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
if (meth == NULL)
|
if (meth == NULL)
|
||||||
return 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);
|
res = PyObject_Call(meth, args, kwds);
|
||||||
Py_LeaveRecursiveCall();
|
|
||||||
|
|
||||||
Py_DECREF(meth);
|
Py_DECREF(meth);
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -132,6 +132,7 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
|
||||||
PyObject *value = *val;
|
PyObject *value = *val;
|
||||||
PyObject *inclass = NULL;
|
PyObject *inclass = NULL;
|
||||||
PyObject *initial_tb = NULL;
|
PyObject *initial_tb = NULL;
|
||||||
|
PyThreadState *tstate = NULL;
|
||||||
|
|
||||||
if (type == NULL) {
|
if (type == NULL) {
|
||||||
/* There was no exception, so nothing to do. */
|
/* There was no exception, so nothing to do. */
|
||||||
|
@ -207,7 +208,14 @@ finally:
|
||||||
Py_DECREF(initial_tb);
|
Py_DECREF(initial_tb);
|
||||||
}
|
}
|
||||||
/* normalize recursively */
|
/* 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);
|
PyErr_NormalizeException(exc, val, tb);
|
||||||
|
--tstate->recursion_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue