summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2012-11-04 13:14:34 (GMT)
committerNick Coghlan <ncoghlan@gmail.com>2012-11-04 13:14:34 (GMT)
commitaab9c2b2ead7b786947c367a9cc5d9c921b9ea99 (patch)
tree323a662a1f65b39e90d28f9f259bc937467691a7 /Lib
parent1321edaa55a8df94e739e6ea5e9e658f2b7bfcc2 (diff)
downloadcpython-aab9c2b2ead7b786947c367a9cc5d9c921b9ea99.zip
cpython-aab9c2b2ead7b786947c367a9cc5d9c921b9ea99.tar.gz
cpython-aab9c2b2ead7b786947c367a9cc5d9c921b9ea99.tar.bz2
Issue #5765: Apply a hard recursion limit in the compiler
Previously, excessive nesting in expressions would blow the stack and segfault the interpreter. Now, a hard limit based on the configured recursion limit and a hardcoded scaling factor is applied.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/crashers/compiler_recursion.py13
-rw-r--r--Lib/test/test_compile.py27
2 files changed, 27 insertions, 13 deletions
diff --git a/Lib/test/crashers/compiler_recursion.py b/Lib/test/crashers/compiler_recursion.py
deleted file mode 100644
index 31f28a9..0000000
--- a/Lib/test/crashers/compiler_recursion.py
+++ /dev/null
@@ -1,13 +0,0 @@
-"""
-The compiler (>= 2.5) recurses happily until it blows the stack.
-
-Recorded on the tracker as http://bugs.python.org/issue11383
-"""
-
-# The variant below blows up in compiler_call, but there are assorted
-# other variations that blow up in other functions
-# e.g. '1*'*10**5+'1' will die in compiler_visit_expr
-
-# The exact limit to destroy the stack will vary by platform
-# but 10M should do the trick even with huge stack allocations
-compile('()'*10**7, '?', 'exec')
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 72342f8..1071f4a 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -474,6 +474,33 @@ if 1:
self.assertInvalidSingle('f()\nxy # blah\nblah()')
self.assertInvalidSingle('x = 5 # comment\nx = 6\n')
+ @support.cpython_only
+ def test_compiler_recursion_limit(self):
+ # Expected limit is sys.getrecursionlimit() * the scaling factor
+ # in symtable.c (currently 3)
+ # We expect to fail *at* that limit, because we use up some of
+ # the stack depth limit in the test suite code
+ # So we check the expected limit and 75% of that
+ # XXX (ncoghlan): duplicating the scaling factor here is a little
+ # ugly. Perhaps it should be exposed somewhere...
+ fail_depth = sys.getrecursionlimit() * 3
+ success_depth = int(fail_depth * 0.75)
+
+ def check_limit(prefix, repeated):
+ expect_ok = prefix + repeated * success_depth
+ self.compile_single(expect_ok)
+ broken = prefix + repeated * fail_depth
+ details = "Compiling ({!r} + {!r} * {})".format(
+ prefix, repeated, fail_depth)
+ with self.assertRaises(RuntimeError, msg=details):
+ self.compile_single(broken)
+
+ check_limit("a", "()")
+ check_limit("a", ".b")
+ check_limit("a", "[0]")
+ check_limit("a", "*a")
+
+
def test_main():
support.run_unittest(TestSpecifics)