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.
diff --git a/Objects/abstract.c b/Objects/abstract.c
index f7a3bfe..7378e0d 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -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,
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index 3d79383..64f655c 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -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__)
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 49b877d..59dec4a 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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;