Skip to content

Commit 0c65e85

Browse files
committed
Keep per-thread recursion and limit and check against global limit on reaching limit.
1 parent 1eca1ff commit 0c65e85

File tree

7 files changed

+20
-24
lines changed

7 files changed

+20
-24
lines changed

Include/cpython/pystate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ struct _ts {
8080
PyInterpreterState *interp;
8181

8282
int recursion_remaining;
83+
int recursion_limit;
8384
int recursion_headroom; /* Allow 50 more calls to handle any errors. */
8485

8586
/* 'tracing' keeps track of the execution depth when tracing/profiling.

Modules/_testinternalcapi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ get_recursion_depth(PyObject *self, PyObject *Py_UNUSED(args))
3838

3939
/* subtract one to ignore the frame of the get_recursion_depth() call */
4040

41-
return PyLong_FromLong(tstate->interp->ceval.recursion_limit - tstate->recursion_remaining - 1);
41+
return PyLong_FromLong(tstate->recursion_limit - tstate->recursion_remaining - 1);
4242
}
4343

4444

Python/ast.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55
#include "Python.h"
66
#include "pycore_ast.h" // asdl_stmt_seq
7-
#include "pycore_interp.h" // recursion limit
87
#include "pycore_pystate.h" // _PyThreadState_GET()
98

109
#include <assert.h>
@@ -934,7 +933,7 @@ _PyAST_Validate(mod_ty mod)
934933
return 0;
935934
}
936935
/* Be careful here to prevent overflow. */
937-
int recursion_depth = tstate->interp->ceval.recursion_limit - tstate->recursion_remaining;
936+
int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
938937
starting_recursion_depth = (recursion_depth< INT_MAX / COMPILER_STACK_FRAME_SCALE) ?
939938
recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth;
940939
state.recursion_depth = starting_recursion_depth;

Python/ast_opt.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#include "Python.h"
33
#include "pycore_ast.h" // _PyAST_GetDocString()
44
#include "pycore_compile.h" // _PyASTOptimizeState
5-
#include "pycore_interp.h" // recursion limit
65
#include "pycore_pystate.h" // _PyThreadState_GET()
76
#include "pycore_format.h" // F_LJUST
87

@@ -1099,7 +1098,7 @@ _PyAST_Optimize(mod_ty mod, PyArena *arena, _PyASTOptimizeState *state)
10991098
return 0;
11001099
}
11011100
/* Be careful here to prevent overflow. */
1102-
int recursion_depth = tstate->interp->ceval.recursion_limit - tstate->recursion_remaining;
1101+
int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
11031102
starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ?
11041103
recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth;
11051104
state->recursion_depth = starting_recursion_depth;

Python/ceval.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -788,13 +788,11 @@ void
788788
Py_SetRecursionLimit(int new_limit)
789789
{
790790
PyInterpreterState *interp = _PyInterpreterState_GET();
791-
int old_limit = interp->ceval.recursion_limit;
792791
interp->ceval.recursion_limit = new_limit;
793792
for (PyThreadState *p = interp->tstate_head; p != NULL; p = p->next) {
794-
if (p->recursion_remaining <= INT_MIN/2) {
795-
p->recursion_remaining = old_limit;
796-
}
797-
p->recursion_remaining += new_limit - old_limit;
793+
int depth = p->recursion_limit - p->recursion_remaining;
794+
p->recursion_limit = new_limit;
795+
p->recursion_remaining = new_limit - depth;
798796
}
799797
}
800798

@@ -803,8 +801,12 @@ Py_SetRecursionLimit(int new_limit)
803801
int
804802
_Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
805803
{
806-
if (tstate->recursion_remaining <= INT_MIN/2) {
807-
tstate->recursion_remaining = tstate->interp->ceval.recursion_limit;
804+
/* Check against global limit first. */
805+
int depth = tstate->recursion_limit - tstate->recursion_remaining;
806+
if (depth < tstate->interp->ceval.recursion_limit) {
807+
tstate->recursion_limit = tstate->interp->ceval.recursion_limit;
808+
tstate->recursion_remaining = tstate->recursion_limit - depth;
809+
assert(tstate->recursion_remaining > 0);
808810
return 0;
809811
}
810812
#ifdef USE_STACKCHECK

Python/pystate.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,8 @@ new_threadstate(PyInterpreterState *interp, int init)
636636

637637
tstate->interp = interp;
638638

639-
tstate->recursion_remaining = INT_MIN/2;
639+
tstate->recursion_limit = interp->ceval.recursion_limit;
640+
tstate->recursion_remaining = interp->ceval.recursion_limit;
640641
tstate->recursion_headroom = 0;
641642
tstate->tracing = 0;
642643
tstate->root_cframe.use_tracing = 0;

Python/sysmodule.c

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,20 +1187,14 @@ sys_setrecursionlimit_impl(PyObject *module, int new_limit)
11871187
return NULL;
11881188
}
11891189

1190-
/* Issue #25274: When the recursion depth hits the recursion limit in
1191-
_Py_CheckRecursiveCall(), the overflowed flag of the thread state is
1192-
set to 1 and a RecursionError is raised. The overflowed flag is reset
1193-
to 0 when the recursion depth goes below the low-water mark: see
1194-
Py_LeaveRecursiveCall().
1195-
1196-
Reject too low new limit if the current recursion depth is higher than
1197-
the new low-water mark. Otherwise it may not be possible anymore to
1198-
reset the overflowed flag to 0. */
1199-
if (tstate->interp->ceval.recursion_limit - tstate->recursion_remaining > new_limit) {
1190+
/* Reject too low new limit if the current recursion depth is higher than
1191+
the new low-water mark. */
1192+
int depth = tstate->recursion_limit - tstate->recursion_remaining;
1193+
if (depth > new_limit) {
12001194
_PyErr_Format(tstate, PyExc_RecursionError,
12011195
"cannot set the recursion limit to %i at "
12021196
"the recursion depth %i: the limit is too low",
1203-
new_limit, tstate->interp->ceval.recursion_limit - tstate->recursion_remaining);
1197+
new_limit, depth);
12041198
return NULL;
12051199
}
12061200

0 commit comments

Comments
 (0)