Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 652e707

Browse files
committed
Issue #5392: when a very low recursion limit was set, the interpreter would
abort with a fatal error after the recursion limit was hit twice.
1 parent ae2dbe2 commit 652e707

3 files changed

Lines changed: 49 additions & 7 deletions

File tree

Include/ceval.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,10 @@ PyAPI_DATA(int) _Py_CheckRecursionLimit;
9292
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
9393
#endif
9494

95-
#ifdef USE_STACKCHECK
96-
# define _Py_MakeEndRecCheck(x) (--(x) < _Py_CheckRecursionLimit - 50)
97-
#else
98-
# define _Py_MakeEndRecCheck(x) (--(x) < _Py_CheckRecursionLimit - 50)
99-
#endif
95+
#define _Py_MakeEndRecCheck(x) \
96+
(--(x) < ((_Py_CheckRecursionLimit > 100) \
97+
? (_Py_CheckRecursionLimit - 50) \
98+
: (3 * (_Py_CheckRecursionLimit >> 2))))
10099

101100
#define Py_ALLOW_RECURSION \
102101
do { unsigned char _old = PyThreadState_GET()->recursion_critical;\

Lib/test/test_sys.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import unittest, test.support
33
import sys, io, os
44
import struct
5+
import subprocess
6+
import textwrap
57

68
class SysModuleTest(unittest.TestCase):
79

@@ -155,6 +157,46 @@ def test_recursionlimit(self):
155157
self.assertEqual(sys.getrecursionlimit(), 10000)
156158
sys.setrecursionlimit(oldlimit)
157159

160+
def test_recursionlimit_recovery(self):
161+
# NOTE: this test is slightly fragile in that it depends on the current
162+
# recursion count when executing the test being low enough so as to
163+
# trigger the recursion recovery detection in the _Py_MakeEndRecCheck
164+
# macro (see ceval.h).
165+
oldlimit = sys.getrecursionlimit()
166+
def f():
167+
f()
168+
try:
169+
for i in (50, 1000):
170+
# Issue #5392: stack overflow after hitting recursion limit twice
171+
sys.setrecursionlimit(i)
172+
self.assertRaises(RuntimeError, f)
173+
self.assertRaises(RuntimeError, f)
174+
finally:
175+
sys.setrecursionlimit(oldlimit)
176+
177+
def test_recursionlimit_fatalerror(self):
178+
# A fatal error occurs if a second recursion limit is hit when recovering
179+
# from a first one.
180+
code = textwrap.dedent("""
181+
import sys
182+
183+
def f():
184+
try:
185+
f()
186+
except RuntimeError:
187+
f()
188+
189+
sys.setrecursionlimit(%d)
190+
f()""")
191+
for i in (50, 1000):
192+
sub = subprocess.Popen([sys.executable, '-c', code % i],
193+
stderr=subprocess.PIPE)
194+
err = sub.communicate()[1]
195+
self.assertTrue(sub.returncode, sub.returncode)
196+
self.assertTrue(
197+
b"Fatal Python error: Cannot recover from stack overflow" in err,
198+
err)
199+
158200
def test_getwindowsversion(self):
159201
if hasattr(sys, "getwindowsversion"):
160202
v = sys.getwindowsversion()

Misc/NEWS

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 2?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #5392: when a very low recursion limit was set, the interpreter would
16+
abort with a fatal error after the recursion limit was hit twice.
17+
1518
Library
1619
-------
1720

@@ -24,8 +27,6 @@ What's New in Python 3.1 alpha 1
2427
Core and Builtins
2528
-----------------
2629

27-
=======
28-
2930
- The io module has been reimplemented in C for speed.
3031

3132
- Give dict views an informative __repr__.

0 commit comments

Comments
 (0)