diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2008-09-03 18:34:34 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2008-09-03 18:34:34 (GMT) |
commit | 658fad8aae60e6c25a102fb56884bf66577366f8 (patch) | |
tree | 641c4687bd0227b36f37fc9d1014358857b27df1 | |
parent | 338f5786eac83f473bcdde27fea6280c762fd92d (diff) | |
download | cpython-658fad8aae60e6c25a102fb56884bf66577366f8.zip cpython-658fad8aae60e6c25a102fb56884bf66577366f8.tar.gz cpython-658fad8aae60e6c25a102fb56884bf66577366f8.tar.bz2 |
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.
-rw-r--r-- | Include/ceval.h | 50 | ||||
-rw-r--r-- | Misc/NEWS | 5 | ||||
-rw-r--r-- | Python/ceval.c | 2 |
3 files changed, 49 insertions, 8 deletions
diff --git a/Include/ceval.h b/Include/ceval.h index f0385cf..919c494 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -42,26 +42,62 @@ PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg); PyAPI_FUNC(int) Py_MakePendingCalls(void); -/* Protection against deeply nested recursive calls */ +/* Protection against deeply nested recursive calls + + In Python 3.0, this protection has two levels: + * normal anti-recursion protection is triggered when the recursion level + exceeds the current recursion limit. It raises a RuntimeError, and sets + the "overflowed" flag in the thread state structure. This flag + temporarily *disables* the normal protection; this allows cleanup code + to potentially outgrow the recursion limit while processing the + RuntimeError. + * "last chance" anti-recursion protection is triggered when the recursion + level exceeds "current recursion limit + 50". By construction, this + protection can only be triggered when the "overflowed" flag is set. It + means the cleanup code has itself gone into an infinite loop, or the + RuntimeError has been mistakingly ignored. When this protection is + triggered, the interpreter aborts with a Fatal Error. + + In addition, the "overflowed" flag is automatically reset when the + recursion level drops below "current recursion limit - 50". This heuristic + is meant to ensure that the normal anti-recursion protection doesn't get + disabled too long. + + Please note: this scheme has its own limitations. See: + http://mail.python.org/pipermail/python-dev/2008-August/082106.html + for some observations. +*/ PyAPI_FUNC(void) Py_SetRecursionLimit(int); PyAPI_FUNC(int) Py_GetRecursionLimit(void); -#define Py_EnterRecursiveCall(where) \ +#define Py_EnterRecursiveCall(where) \ (_Py_MakeRecCheck(PyThreadState_GET()->recursion_depth) && \ _Py_CheckRecursiveCall(where)) #define Py_LeaveRecursiveCall() \ - do{ if((--PyThreadState_GET()->recursion_depth) < \ - _Py_CheckRecursionLimit - 50) \ - PyThreadState_GET()->overflowed = 0; \ - } while(0) + do{ if(_Py_MakeEndRecCheck(PyThreadState_GET()->recursion_depth)) \ + PyThreadState_GET()->overflowed = 0; \ + } while(0) PyAPI_FUNC(int) _Py_CheckRecursiveCall(char *where); PyAPI_DATA(int) _Py_CheckRecursionLimit; + #ifdef USE_STACKCHECK -# define _Py_MakeRecCheck(x) (++(x) > --_Py_CheckRecursionLimit) +/* With USE_STACKCHECK, we artificially decrement the recursion limit in order + to trigger regular stack checks in _Py_CheckRecursiveCall(), except if + the "overflowed" flag is set, in which case we need the true value + of _Py_CheckRecursionLimit for _Py_MakeEndRecCheck() to function properly. +*/ +# define _Py_MakeRecCheck(x) \ + (++(x) > (_Py_CheckRecursionLimit += PyThreadState_GET()->overflowed - 1)) #else # define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit) #endif +#ifdef USE_STACKCHECK +# define _Py_MakeEndRecCheck(x) (--(x) < _Py_CheckRecursionLimit - 50) +#else +# define _Py_MakeEndRecCheck(x) (--(x) < _Py_CheckRecursionLimit - 50) +#endif + #define Py_ALLOW_RECURSION \ do { unsigned char _old = PyThreadState_GET()->recursion_critical;\ PyThreadState_GET()->recursion_critical = 1; @@ -12,6 +12,11 @@ What's New in Python 3.0 release candidate 1 Core and Builtins ----------------- +- 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. + - Issue 3639: The _warnings module could segfault the interpreter when unexpected types were passed in as arguments. diff --git a/Python/ceval.c b/Python/ceval.c index 42df3cb..dc4276b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -471,6 +471,7 @@ _Py_CheckRecursiveCall(char *where) return -1; } #endif + _Py_CheckRecursionLimit = recursion_limit; if (tstate->recursion_critical) /* Somebody asked that we don't check for recursion. */ return 0; @@ -489,7 +490,6 @@ _Py_CheckRecursiveCall(char *where) where); return -1; } - _Py_CheckRecursionLimit = recursion_limit; return 0; } |