Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 658fad8

Browse files
committed
Issue #3697: "Fatal Python error: Cannot recover from stack overflow"
could be easily encountered under Windows in debug mode when exercising the recursion limit checking code, due to bogus handling of recursion limit when USE_STACKCHEK was enabled. Reviewed by Amaury Forgeot d'Arc on IRC.
1 parent 338f578 commit 658fad8

3 files changed

Lines changed: 49 additions & 8 deletions

File tree

Include/ceval.h

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,62 @@ PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf);
4242
PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg);
4343
PyAPI_FUNC(int) Py_MakePendingCalls(void);
4444

45-
/* Protection against deeply nested recursive calls */
45+
/* Protection against deeply nested recursive calls
46+
47+
In Python 3.0, this protection has two levels:
48+
* normal anti-recursion protection is triggered when the recursion level
49+
exceeds the current recursion limit. It raises a RuntimeError, and sets
50+
the "overflowed" flag in the thread state structure. This flag
51+
temporarily *disables* the normal protection; this allows cleanup code
52+
to potentially outgrow the recursion limit while processing the
53+
RuntimeError.
54+
* "last chance" anti-recursion protection is triggered when the recursion
55+
level exceeds "current recursion limit + 50". By construction, this
56+
protection can only be triggered when the "overflowed" flag is set. It
57+
means the cleanup code has itself gone into an infinite loop, or the
58+
RuntimeError has been mistakingly ignored. When this protection is
59+
triggered, the interpreter aborts with a Fatal Error.
60+
61+
In addition, the "overflowed" flag is automatically reset when the
62+
recursion level drops below "current recursion limit - 50". This heuristic
63+
is meant to ensure that the normal anti-recursion protection doesn't get
64+
disabled too long.
65+
66+
Please note: this scheme has its own limitations. See:
67+
http://mail.python.org/pipermail/python-dev/2008-August/082106.html
68+
for some observations.
69+
*/
4670
PyAPI_FUNC(void) Py_SetRecursionLimit(int);
4771
PyAPI_FUNC(int) Py_GetRecursionLimit(void);
4872

49-
#define Py_EnterRecursiveCall(where) \
73+
#define Py_EnterRecursiveCall(where) \
5074
(_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \
5175
_Py_CheckRecursiveCall(where))
5276
#define Py_LeaveRecursiveCall() \
53-
do{ if((--PyThreadState_GET()->recursion_depth) < \
54-
_Py_CheckRecursionLimit - 50) \
55-
PyThreadState_GET()->overflowed = 0; \
56-
} while(0)
77+
do{ if(_Py_MakeEndRecCheck(PyThreadState_GET()->recursion_depth)) \
78+
PyThreadState_GET()->overflowed = 0; \
79+
} while(0)
5780
PyAPI_FUNC(int) _Py_CheckRecursiveCall(char *where);
5881
PyAPI_DATA(int) _Py_CheckRecursionLimit;
82+
5983
#ifdef USE_STACKCHECK
60-
# define _Py_MakeRecCheck(x) (++(x) > --_Py_CheckRecursionLimit)
84+
/* With USE_STACKCHECK, we artificially decrement the recursion limit in order
85+
to trigger regular stack checks in _Py_CheckRecursiveCall(), except if
86+
the "overflowed" flag is set, in which case we need the true value
87+
of _Py_CheckRecursionLimit for _Py_MakeEndRecCheck() to function properly.
88+
*/
89+
# define _Py_MakeRecCheck(x) \
90+
(++(x) > (_Py_CheckRecursionLimit += PyThreadState_GET()->overflowed - 1))
6191
#else
6292
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
6393
#endif
6494

95+
#ifdef USE_STACKCHECK
96+
# define _Py_MakeEndRecCheck(x) (--(x) < _Py_CheckRecursionLimit - 50)
97+
#else
98+
# define _Py_MakeEndRecCheck(x) (--(x) < _Py_CheckRecursionLimit - 50)
99+
#endif
100+
65101
#define Py_ALLOW_RECURSION \
66102
do { unsigned char _old = PyThreadState_GET()->recursion_critical;\
67103
PyThreadState_GET()->recursion_critical = 1;

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ What's New in Python 3.0 release candidate 1
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #3697: "Fatal Python error: Cannot recover from stack overflow"
16+
could be easily encountered under Windows in debug mode when exercising
17+
the recursion limit checking code, due to bogus handling of recursion
18+
limit when USE_STACKCHEK was enabled.
19+
1520
- Issue 3639: The _warnings module could segfault the interpreter when
1621
unexpected types were passed in as arguments.
1722

Python/ceval.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ _Py_CheckRecursiveCall(char *where)
471471
return -1;
472472
}
473473
#endif
474+
_Py_CheckRecursionLimit = recursion_limit;
474475
if (tstate->recursion_critical)
475476
/* Somebody asked that we don't check for recursion. */
476477
return 0;
@@ -489,7 +490,6 @@ _Py_CheckRecursiveCall(char *where)
489490
where);
490491
return -1;
491492
}
492-
_Py_CheckRecursionLimit = recursion_limit;
493493
return 0;
494494
}
495495

0 commit comments

Comments
 (0)