/* --- _Py_EnterRecursiveCall() ----------------------------------------- */
-static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
+static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
uintptr_t here_addr = _Py_get_machine_stack_pointer();
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
- // Possible overflow if stack pointer is beyond the soft limit.
- // _Py_CheckRecursiveCall will check for corner cases and
- // report an error if there is an overflow.
+ // Overflow if stack pointer is between soft limit and the base of the hardware stack.
+ // If it is below the hardware stack base, assume that we have the wrong stack limits, and do nothing.
+ // We could have the wrong stack limits because of limited platform support, or user-space threads.
#if _Py_STACK_GROWS_DOWN
- return here_addr < _tstate->c_stack_soft_limit;
+ return here_addr < _tstate->c_stack_soft_limit && here_addr >= _tstate->c_stack_soft_limit - 2 * _PyOS_STACK_MARGIN_BYTES;
#else
- return here_addr > _tstate->c_stack_soft_limit;
+ return here_addr > _tstate->c_stack_soft_limit && here_addr <= _tstate->c_stack_soft_limit + 2 * _PyOS_STACK_MARGIN_BYTES;
#endif
}
static inline int _Py_EnterRecursiveCallTstate(PyThreadState *tstate,
const char *where) {
- return (_Py_ReachedRecursionLimit(tstate) && _Py_CheckRecursiveCall(tstate, where));
+ return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where));
}
static inline int _Py_EnterRecursiveCall(const char *where) {
PyAPI_FUNC(void) _Py_InitializeRecursionLimits(PyThreadState *tstate);
+PyAPI_FUNC(int) _Py_ReachedRecursionLimit(PyThreadState *tstate);
+
// Export for test_peg_generator
PyAPI_FUNC(int) _Py_ReachedRecursionLimitWithMargin(
PyThreadState *tstate,
#define _Py_AssertHoldsTstate()
#endif
+#if !_Py__has_builtin(__builtin_frame_address) && !defined(__GNUC__) && !defined(_MSC_VER)
+static uintptr_t return_pointer_as_int(char* p) {
+ return (uintptr_t)p;
+}
+#endif
static inline uintptr_t
_Py_get_machine_stack_pointer(void) {
- uintptr_t result;
-#if !defined(_MSC_VER) && defined(_M_ARM64)
- result = __getReg(31);
-#elif defined(_MSC_VER) && defined(_M_X64)
- result = (uintptr_t)_AddressOfReturnAddress();
-#elif defined(__aarch64__)
- __asm__ ("mov %0, sp" : "=r" (result));
-#elif defined(__x86_64__)
- __asm__("{movq %%rsp, %0" : "=r" (result));
+#if _Py__has_builtin(__builtin_frame_address) || defined(__GNUC__)
+ return (uintptr_t)__builtin_frame_address(0);
+#elif defined(_MSC_VER)
+ return (uintptr_t)_AddressOfReturnAddress();
#else
char here;
- result = (uintptr_t)&here;
+ /* Avoid compiler warning about returning stack address */
+ return return_pointer_as_int(&here);
#endif
- return result;
}
static inline intptr_t
* stack consumption of PyEval_EvalDefault */
#if (defined(Py_DEBUG) \
|| defined(_Py_ADDRESS_SANITIZER) \
- || defined(_Py_THREAD_SANITIZER)) \
- || defined(_Py_UNDEFINED_BEHAVIOR_SANITIZER)
+ || defined(_Py_THREAD_SANITIZER))
# define _PyOS_LOG2_STACK_MARGIN 12
#else
# define _PyOS_LOG2_STACK_MARGIN 11
# define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
# endif
# endif
-# if __has_feature(undefined_behavior_sanitizer)
-# if !defined(_Py_UNDEFINED_BEHAVIOR_SANITIZER)
-# define _Py_UNDEFINED_BEHAVIOR_SANITIZER
-# endif
-# endif
#elif defined(__GNUC__)
# if defined(__SANITIZE_ADDRESS__)
# define _Py_ADDRESS_SANITIZER
def test_deeply_nested_content_model(self):
# This should raise a RecursionError and not crash.
# See https://github.com/python/cpython/issues/145986.
- N = 800_000
+ N = 500_000
data = (
b'<!DOCTYPE root [\n<!ELEMENT root '
+ b'(a, ' * N + b'a' + b')' * N
#endif
}
+void
+_Py_EnterRecursiveCallUnchecked(PyThreadState *tstate)
+{
+ uintptr_t here_addr = _Py_get_machine_stack_pointer();
+ _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
+#if _Py_STACK_GROWS_DOWN
+ if (here_addr < _tstate->c_stack_hard_limit) {
+#else
+ if (here_addr > _tstate->c_stack_hard_limit) {
+#endif
+ Py_FatalError("Unchecked stack overflow.");
+ }
+}
+
#if defined(__s390x__)
# define Py_C_STACK_SIZE 320000
#elif defined(_WIN32)
/* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall()
- if the stack pointer is beyond c_stack_soft_limit. */
+ if the stack pointer is between the stack base and c_stack_hard_limit. */
int
_Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
{
assert(_tstate->c_stack_soft_limit != 0);
assert(_tstate->c_stack_hard_limit != 0);
#if _Py_STACK_GROWS_DOWN
+ assert(here_addr >= _tstate->c_stack_hard_limit - _PyOS_STACK_MARGIN_BYTES);
if (here_addr < _tstate->c_stack_hard_limit) {
- if (here_addr < _tstate->c_stack_hard_limit - _PyOS_STACK_MARGIN_BYTES) {
- // Far out of bounds -- Assume stack switching has occurred
- return 0;
- }
+ /* Overflowing while handling an overflow. Give up. */
int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
#else
+ assert(here_addr <= _tstate->c_stack_hard_limit + _PyOS_STACK_MARGIN_BYTES);
if (here_addr > _tstate->c_stack_hard_limit) {
- if (here_addr > _tstate->c_stack_hard_limit + _PyOS_STACK_MARGIN_BYTES) {
- // Far out of bounds -- Assume stack switching has occurred
- return 0;
- }
+ /* Overflowing while handling an overflow. Give up. */
int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
#endif
- /* Too much stack used to safely raise an exception. Give up. */
char buffer[80];
snprintf(buffer, 80, "Unrecoverable stack overflow (used %d kB)%s", kbytes_used, where);
Py_FatalError(buffer);
return PyStackRef_FromPyObjectSteal(iter_o);
}
+Py_NO_INLINE int
+_Py_ReachedRecursionLimit(PyThreadState *tstate) {
+ uintptr_t here_addr = _Py_get_machine_stack_pointer();
+ _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
+ assert(_tstate->c_stack_hard_limit != 0);
+#if _Py_STACK_GROWS_DOWN
+ return here_addr <= _tstate->c_stack_soft_limit;
+#else
+ return here_addr >= _tstate->c_stack_soft_limit;
+#endif
+}
+
+
#if (defined(__GNUC__) && __GNUC__ >= 10 && !defined(__clang__)) && defined(__x86_64__)
/*
* gh-129987: The SLP autovectorizer can cause poor code generation for
return 0;
}
-/* One-off compilation of the jit entry shim.
+/* One-off compilation of the jit entry shim
* We compile this once only as it effectively a normal
* function, but we need to use the JIT because it needs
* to understand the jit-specific calling convention.