You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Recursive calls crash interpreter when checking exceptions #86675
The following program 1 can crash in Python 3. We have reproduce it in the Python version 3.5, 3.6, 3.7, 3.8, 3.9, 3.10. This bug seems to be similar to bpo-36272, however, the tracking system shows bpo-36272 has been fixed and the program 2, which triggers bpo-36272, will not cause crash (“core dump”) in Python 3.8, 3.9, 3.10.
We have attached the stack trace in the end of this report.
======================
The error message is like following:
“
ERROR:root:foo
ERROR:root:foo
ERROR:root:foo
ERROR:root:foo
…
ERROR:root:foo
ERROR:root:foo
ERROR:root:foo
Fatal Python error: _Py_CheckRecursiveCall: Cannot recover from stack overflow.
Python runtime state: initialized
Current thread 0x00007f0fa440b700 (most recent call first):
File "/usr/local/python310/lib/python3.10/logging/init.py", line 420 in usesTime
File "/usr/local/python310/lib/python3.10/logging/init.py", line 643 in usesTime
File "/usr/local/python310/lib/python3.10/logging/init.py", line 675 in format
File "/usr/local/python310/lib/python3.10/logging/init.py", line 938 in format
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1094 in emit
File "/usr/local/python310/lib/python3.10/logging/init.py", line 963 in handle
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1673 in callHandlers
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1611 in handle
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1601 in _log
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1483 in error
File "/usr/local/python310/lib/python3.10/logging/init.py", line 2080 in error
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 8 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
….
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
...
Aborted (core dumped)”
If you already caught a RecursionError and you keep recursing anyway, once you go 50 levels beyond sys.getrecursionlimit(), the interpreter crashes regardless of what is excepted. In /Python/ceval.c, there's this:
if (tstate->overflowed) {
if (tstate->recursion_depth>recursion_limit+50) {
/* Overflowing while handling an overflow. Give up. */Py_FatalError("Cannot recover from stack overflow.");
}
return0;
}
In your Program 2, when the interpreter raises a RecursionError, it is raised normally and everything is fine.
In your Program 1, when the interpreter raises a RecursionError, it is excepted, so the interpreter thinks it's okay to keep going, and when it does, it raises more RecursionErrors, which it keeps excepting, until it finally can't go any farther ( > 50 + sys.getrecursionlimit()), and has no option but to crash.
"Cannot recover from stack overflow." seems to make sense to me: when the interpreter tries to recover, the code won't let it.
But program like following program 3 will not cause any core dump, RecursionError is also being caught in this Recursion.
program 3
def rec():
try:
rec()
except:
pass
rec()
Beside,I use sys.setrecursionlimit(80), and the program 1 still cause core dump.I print sys.getrecursionlimit(),the value is 1000. 80 is << 50 +1000, it shouldn't cause core dump.
sys.getrecursionlimit() returns whatever was passed to the most recent call of sys.setrecursionlimit(...), with some system default (here 1000).
Catching a RecursionError might be fine sometimes, but the issue is that Program 1 catches a RecursionError *and then keeps recursing more* rather than stopping.
I think it might have to be the responsibility of the Python user to make sure that if a RecursionError is to be caught, that the program can recover without making things much worse. It's my understanding that the extra buffer of +50 is to make sure that the programmer has room to stop the overflow and do any necessary cleanup [1].
If no attempt is made to clean up, then it seems reasonable to me that Python should crash, unless there's some idea of what could happen that I'm missing. The interpreter could allow arbitrary recursion during the cleanup until the C stack overflows, but that sort of defeats the point of the recursion checker. It could raise some new ExtraSuperRecurionError, but that doesn't fix anything: what if *that* error is caught ;) ?
Activity
xxm commentedon Nov 30, 2020
The following program 1 can crash in Python 3. We have reproduce it in the Python version 3.5, 3.6, 3.7, 3.8, 3.9, 3.10. This bug seems to be similar to bpo-36272, however, the tracking system shows bpo-36272 has been fixed and the program 2, which triggers bpo-36272, will not cause crash (“core dump”) in Python 3.8, 3.9, 3.10.
We have attached the stack trace in the end of this report.
Program 1:
========================
========================
Program 2:
========================
======================
The error message is like following:
“
ERROR:root:foo
ERROR:root:foo
ERROR:root:foo
ERROR:root:foo
…
ERROR:root:foo
ERROR:root:foo
ERROR:root:foo
Fatal Python error: _Py_CheckRecursiveCall: Cannot recover from stack overflow.
Python runtime state: initialized
Current thread 0x00007f0fa440b700 (most recent call first):
File "/usr/local/python310/lib/python3.10/logging/init.py", line 420 in usesTime
File "/usr/local/python310/lib/python3.10/logging/init.py", line 643 in usesTime
File "/usr/local/python310/lib/python3.10/logging/init.py", line 675 in format
File "/usr/local/python310/lib/python3.10/logging/init.py", line 938 in format
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1094 in emit
File "/usr/local/python310/lib/python3.10/logging/init.py", line 963 in handle
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1673 in callHandlers
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1611 in handle
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1601 in _log
File "/usr/local/python310/lib/python3.10/logging/init.py", line 1483 in error
File "/usr/local/python310/lib/python3.10/logging/init.py", line 2080 in error
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 8 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
….
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
File "/home/xxm/Desktop/methodfuzzer/error/loggingtest.py", line 12 in rec
...
Aborted (core dumped)”
sweeneyde commentedon Nov 30, 2020
This might be the expected behavior. See https://bugs.python.org/issue25222
If you already caught a RecursionError and you keep recursing anyway, once you go 50 levels beyond sys.getrecursionlimit(), the interpreter crashes regardless of what is
excepted. In /Python/ceval.c, there's this:In your Program 2, when the interpreter raises a
RecursionError, it is raised normally and everything is fine.In your Program 1, when the interpreter raises a
RecursionError, it isexcepted, so the interpreter thinks it's okay to keep going, and when it does, it raises moreRecursionErrors, which it keepsexcepting, until it finally can't go any farther ( > 50 + sys.getrecursionlimit()), and has no option but to crash."Cannot recover from stack overflow." seems to make sense to me: when the interpreter tries to recover, the code won't let it.
ronaldoussoren commentedon Nov 30, 2020
See also bpo-42500
xxm commentedon Nov 30, 2020
But program like following program 3 will not cause any core dump, RecursionError is also being caught in this Recursion.
program 3
def rec():
try:
rec()
except:
pass
rec()
Beside,I use sys.setrecursionlimit(80), and the program 1 still cause core dump.I print sys.getrecursionlimit(),the value is 1000. 80 is << 50 +1000, it shouldn't cause core dump.
sweeneyde commentedon Nov 30, 2020
sys.getrecursionlimit() returns whatever was passed to the most recent call of sys.setrecursionlimit(...), with some system default (here 1000).
Catching a RecursionError might be fine sometimes, but the issue is that Program 1 catches a RecursionError *and then keeps recursing more* rather than stopping.
I think it might have to be the responsibility of the Python user to make sure that if a RecursionError is to be caught, that the program can recover without making things much worse. It's my understanding that the extra buffer of +50 is to make sure that the programmer has room to stop the overflow and do any necessary cleanup [1].
If no attempt is made to clean up, then it seems reasonable to me that Python should crash, unless there's some idea of what could happen that I'm missing. The interpreter could allow arbitrary recursion during the cleanup until the C stack overflows, but that sort of defeats the point of the recursion checker. It could raise some new ExtraSuperRecurionError, but that doesn't fix anything: what if *that* error is caught ;) ?
[1] to get back to a recursion depth lower than some lower threshold: https://github.com/python/cpython/blob/master/Include/internal/pycore_ceval.h#L98
markshannon commentedon Nov 30, 2020
Duplicate of 42500