summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2009-03-13 19:25:20 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2009-03-13 19:25:20 (GMT)
commit652e7076fee59d92d19a0d6e326b9069a2aa09e4 (patch)
tree5287ec21422abfbe642d9ff2138cebac2ac04a00
parentae2dbe2543859f9ad0ff0a2e84b33421c8fd5406 (diff)
downloadcpython-652e7076fee59d92d19a0d6e326b9069a2aa09e4.zip
cpython-652e7076fee59d92d19a0d6e326b9069a2aa09e4.tar.gz
cpython-652e7076fee59d92d19a0d6e326b9069a2aa09e4.tar.bz2
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.
-rw-r--r--Include/ceval.h9
-rw-r--r--Lib/test/test_sys.py42
-rw-r--r--Misc/NEWS5
3 files changed, 49 insertions, 7 deletions
diff --git a/Include/ceval.h b/Include/ceval.h
index 919c494..7bd8179 100644
--- a/Include/ceval.h
+++ b/Include/ceval.h
@@ -92,11 +92,10 @@ PyAPI_DATA(int) _Py_CheckRecursionLimit;
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
#endif
-#ifdef USE_STACKCHECK
-# define _Py_MakeEndRecCheck(x) (--(x) < _Py_CheckRecursionLimit - 50)
-#else
-# define _Py_MakeEndRecCheck(x) (--(x) < _Py_CheckRecursionLimit - 50)
-#endif
+#define _Py_MakeEndRecCheck(x) \
+ (--(x) < ((_Py_CheckRecursionLimit > 100) \
+ ? (_Py_CheckRecursionLimit - 50) \
+ : (3 * (_Py_CheckRecursionLimit >> 2))))
#define Py_ALLOW_RECURSION \
do { unsigned char _old = PyThreadState_GET()->recursion_critical;\
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index fad9939..9f0c139 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -2,6 +2,8 @@
import unittest, test.support
import sys, io, os
import struct
+import subprocess
+import textwrap
class SysModuleTest(unittest.TestCase):
@@ -155,6 +157,46 @@ class SysModuleTest(unittest.TestCase):
self.assertEqual(sys.getrecursionlimit(), 10000)
sys.setrecursionlimit(oldlimit)
+ def test_recursionlimit_recovery(self):
+ # NOTE: this test is slightly fragile in that it depends on the current
+ # recursion count when executing the test being low enough so as to
+ # trigger the recursion recovery detection in the _Py_MakeEndRecCheck
+ # macro (see ceval.h).
+ oldlimit = sys.getrecursionlimit()
+ def f():
+ f()
+ try:
+ for i in (50, 1000):
+ # Issue #5392: stack overflow after hitting recursion limit twice
+ sys.setrecursionlimit(i)
+ self.assertRaises(RuntimeError, f)
+ self.assertRaises(RuntimeError, f)
+ finally:
+ sys.setrecursionlimit(oldlimit)
+
+ def test_recursionlimit_fatalerror(self):
+ # A fatal error occurs if a second recursion limit is hit when recovering
+ # from a first one.
+ code = textwrap.dedent("""
+ import sys
+
+ def f():
+ try:
+ f()
+ except RuntimeError:
+ f()
+
+ sys.setrecursionlimit(%d)
+ f()""")
+ for i in (50, 1000):
+ sub = subprocess.Popen([sys.executable, '-c', code % i],
+ stderr=subprocess.PIPE)
+ err = sub.communicate()[1]
+ self.assertTrue(sub.returncode, sub.returncode)
+ self.assertTrue(
+ b"Fatal Python error: Cannot recover from stack overflow" in err,
+ err)
+
def test_getwindowsversion(self):
if hasattr(sys, "getwindowsversion"):
v = sys.getwindowsversion()
diff --git a/Misc/NEWS b/Misc/NEWS
index 70ad5e2..4468df6 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 2?
Core and Builtins
-----------------
+- 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.
+
Library
-------
@@ -24,8 +27,6 @@ What's New in Python 3.1 alpha 1
Core and Builtins
-----------------
-=======
-
- The io module has been reimplemented in C for speed.
- Give dict views an informative __repr__.