Skip to content

Commit 17b73ab

Browse files
authored
GH-113655: Lower the C recursion limit on various platforms (GH-113944)
1 parent 6c502ba commit 17b73ab

File tree

13 files changed

+41
-39
lines changed

13 files changed

+41
-39
lines changed

Include/cpython/pystate.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,14 @@ struct _ts {
224224
// recursions, sometimes less. 500 is a more conservative limit.
225225
# define Py_C_RECURSION_LIMIT 500
226226
#elif defined(__s390x__)
227-
# define Py_C_RECURSION_LIMIT 1200
227+
# define Py_C_RECURSION_LIMIT 800
228+
#elif defined(_WIN32)
229+
# define Py_C_RECURSION_LIMIT 4000
230+
#elif defined(_Py_ADDRESS_SANITIZER)
231+
# define Py_C_RECURSION_LIMIT 4000
228232
#else
229233
// This value is duplicated in Lib/test/support/__init__.py
230-
# define Py_C_RECURSION_LIMIT 8000
234+
# define Py_C_RECURSION_LIMIT 10000
231235
#endif
232236

233237

Lib/test/support/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2377,7 +2377,10 @@ def _get_c_recursion_limit():
23772377
return _testcapi.Py_C_RECURSION_LIMIT
23782378
except (ImportError, AttributeError):
23792379
# Originally taken from Include/cpython/pystate.h .
2380-
return 8000
2380+
if sys.platform == 'win32':
2381+
return 4000
2382+
else:
2383+
return 10000
23812384

23822385
# The default C recursion limit.
23832386
Py_C_RECURSION_LIMIT = _get_c_recursion_limit()

Lib/test/test_ast.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,7 @@ def next(self):
11261126
def test_ast_recursion_limit(self):
11271127
fail_depth = support.EXCEEDS_RECURSION_LIMIT
11281128
crash_depth = 100_000
1129-
success_depth = 1200
1129+
success_depth = int(support.Py_C_RECURSION_LIMIT * 0.8)
11301130
if _testinternalcapi is not None:
11311131
remaining = _testinternalcapi.get_c_recursion_remaining()
11321132
success_depth = min(success_depth, remaining)

Lib/test/test_compile.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -623,12 +623,10 @@ def test_yet_more_evil_still_undecodable(self):
623623
@support.cpython_only
624624
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
625625
def test_compiler_recursion_limit(self):
626-
# Expected limit is Py_C_RECURSION_LIMIT * 2
627-
# Duplicating the limit here is a little ugly.
628-
# Perhaps it should be exposed somewhere...
629-
fail_depth = Py_C_RECURSION_LIMIT * 2 + 1
626+
# Expected limit is Py_C_RECURSION_LIMIT
627+
fail_depth = Py_C_RECURSION_LIMIT + 1
630628
crash_depth = Py_C_RECURSION_LIMIT * 100
631-
success_depth = int(Py_C_RECURSION_LIMIT * 1.8)
629+
success_depth = int(Py_C_RECURSION_LIMIT * 0.8)
632630

633631
def check_limit(prefix, repeated, mode="single"):
634632
expect_ok = prefix + repeated * success_depth

Lib/test/test_functools.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1875,8 +1875,14 @@ def fib(n):
18751875
return fib(n-1) + fib(n-2)
18761876

18771877
if not support.Py_DEBUG:
1878+
depth = support.Py_C_RECURSION_LIMIT*2//7
18781879
with support.infinite_recursion():
1879-
fib(2500)
1880+
fib(depth)
1881+
if self.module == c_functools:
1882+
fib.cache_clear()
1883+
with support.infinite_recursion():
1884+
with self.assertRaises(RecursionError):
1885+
fib(10000)
18801886

18811887

18821888
@py_functools.lru_cache()

Lib/test/test_sys_settrace.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3037,10 +3037,8 @@ def test_trace_unpack_long_sequence(self):
30373037
self.assertEqual(counts, {'call': 1, 'line': 301, 'return': 1})
30383038

30393039
def test_trace_lots_of_globals(self):
3040-
count = 1000
3041-
if _testinternalcapi is not None:
3042-
remaining = _testinternalcapi.get_c_recursion_remaining()
3043-
count = min(count, remaining)
3040+
3041+
count = min(1000, int(support.Py_C_RECURSION_LIMIT * 0.8))
30443042

30453043
code = """if 1:
30463044
def f():
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Set the C recursion limit to 4000 on Windows, and 10000 on Linux/OSX. This
2+
seems to be near the sweet spot to maintain safety, but not compromise
3+
backwards compatibility.

Parser/asdl_c.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,15 +1388,14 @@ class PartingShots(StaticVisitor):
13881388
13891389
int starting_recursion_depth;
13901390
/* Be careful here to prevent overflow. */
1391-
int COMPILER_STACK_FRAME_SCALE = 2;
13921391
PyThreadState *tstate = _PyThreadState_GET();
13931392
if (!tstate) {
13941393
return NULL;
13951394
}
13961395
struct validator vstate;
1397-
vstate.recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
1396+
vstate.recursion_limit = Py_C_RECURSION_LIMIT;
13981397
int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
1399-
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
1398+
starting_recursion_depth = recursion_depth;
14001399
vstate.recursion_depth = starting_recursion_depth;
14011400
14021401
PyObject *result = ast2obj_mod(state, &vstate, t);

Python/Python-ast.c

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/ast.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,10 +1037,6 @@ validate_type_params(struct validator *state, asdl_type_param_seq *tps)
10371037
return 1;
10381038
}
10391039

1040-
1041-
/* See comments in symtable.c. */
1042-
#define COMPILER_STACK_FRAME_SCALE 2
1043-
10441040
int
10451041
_PyAST_Validate(mod_ty mod)
10461042
{
@@ -1057,9 +1053,9 @@ _PyAST_Validate(mod_ty mod)
10571053
}
10581054
/* Be careful here to prevent overflow. */
10591055
int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
1060-
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
1056+
starting_recursion_depth = recursion_depth;
10611057
state.recursion_depth = starting_recursion_depth;
1062-
state.recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
1058+
state.recursion_limit = Py_C_RECURSION_LIMIT;
10631059

10641060
switch (mod->kind) {
10651061
case Module_kind:

0 commit comments

Comments
 (0)