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

+6-2
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

+4-1
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

+1-1
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

+3-5
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

+7-1
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

+2-4
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():
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

+2-3
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

+2-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/ast.c

+2-6
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:

Python/ast_opt.c

+2-5
Original file line numberDiff line numberDiff line change
@@ -1100,9 +1100,6 @@ astfold_type_param(type_param_ty node_, PyArena *ctx_, _PyASTOptimizeState *stat
11001100
#undef CALL_OPT
11011101
#undef CALL_SEQ
11021102

1103-
/* See comments in symtable.c. */
1104-
#define COMPILER_STACK_FRAME_SCALE 2
1105-
11061103
int
11071104
_PyAST_Optimize(mod_ty mod, PyArena *arena, int optimize, int ff_features)
11081105
{
@@ -1120,9 +1117,9 @@ _PyAST_Optimize(mod_ty mod, PyArena *arena, int optimize, int ff_features)
11201117
}
11211118
/* Be careful here to prevent overflow. */
11221119
int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
1123-
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
1120+
starting_recursion_depth = recursion_depth;
11241121
state.recursion_depth = starting_recursion_depth;
1125-
state.recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
1122+
state.recursion_limit = Py_C_RECURSION_LIMIT;
11261123

11271124
int ret = astfold_mod(mod, arena, &state);
11281125
assert(ret || PyErr_Occurred());

Python/symtable.c

+2-7
Original file line numberDiff line numberDiff line change
@@ -386,11 +386,6 @@ symtable_new(void)
386386
return NULL;
387387
}
388388

389-
/* Using a scaling factor means this should automatically adjust when
390-
the recursion limit is adjusted for small or large C stack allocations.
391-
*/
392-
#define COMPILER_STACK_FRAME_SCALE 2
393-
394389
struct symtable *
395390
_PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future)
396391
{
@@ -417,9 +412,9 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future)
417412
}
418413
/* Be careful here to prevent overflow. */
419414
int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
420-
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
415+
starting_recursion_depth = recursion_depth;
421416
st->recursion_depth = starting_recursion_depth;
422-
st->recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
417+
st->recursion_limit = Py_C_RECURSION_LIMIT;
423418

424419
/* Make the initial symbol information gathering pass */
425420
if (!symtable_enter_block(st, &_Py_ID(top), ModuleBlock, (void *)mod, 0, 0, 0, 0)) {

Python/traceback.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,11 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
965965
unsigned int depth = 0;
966966
while (1) {
967967
if (MAX_FRAME_DEPTH <= depth) {
968-
PUTS(fd, " ...\n");
968+
if (MAX_FRAME_DEPTH < depth) {
969+
PUTS(fd, "plus ");
970+
_Py_DumpDecimal(fd, depth);
971+
PUTS(fd, " frames\n");
972+
}
969973
break;
970974
}
971975
dump_frame(fd, frame);

0 commit comments

Comments
 (0)