]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-126910: Make `_Py_get_machine_stack_pointer` return the actual stack pointer ...
authorMark Shannon <Mark.Shannon@arm.com>
Tue, 26 May 2026 14:14:17 +0000 (15:14 +0100)
committerGitHub <noreply@github.com>
Tue, 26 May 2026 14:14:17 +0000 (15:14 +0100)
* Make _Py_ReachedRecursionLimit inline again
* Remove _Py_MakeRecCheck replacing its use with _Py_ReachedRecursionLimit
* Move the check for C stack swtiching into _Py_CheckRecursiveCall

Include/internal/pycore_ceval.h
Include/internal/pycore_pystate.h
Include/internal/pycore_pythonrun.h
Lib/test/test_pyexpat.py
Python/ceval.c
Python/jit.c

index fd4221f0816d24b9291875305961507385ac832f..06c4ca1619d7ce16be86c4d3c58cb257c438aa81 100644 (file)
@@ -208,16 +208,16 @@ extern void _PyEval_DeactivateOpCache(void);
 
 /* --- _Py_EnterRecursiveCall() ----------------------------------------- */
 
-static inline int _Py_MakeRecCheck(PyThreadState *tstate)  {
+static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate)  {
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
-    // 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.
+    // 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.
 #if _Py_STACK_GROWS_DOWN
-    return here_addr < _tstate->c_stack_soft_limit && here_addr >= _tstate->c_stack_soft_limit - 2 * _PyOS_STACK_MARGIN_BYTES;
+    return here_addr < _tstate->c_stack_soft_limit;
 #else
-    return here_addr > _tstate->c_stack_soft_limit && here_addr <= _tstate->c_stack_soft_limit + 2 * _PyOS_STACK_MARGIN_BYTES;
+    return here_addr > _tstate->c_stack_soft_limit;
 #endif
 }
 
@@ -232,7 +232,7 @@ PyAPI_FUNC(int) _Py_CheckRecursiveCallPy(
 
 static inline int _Py_EnterRecursiveCallTstate(PyThreadState *tstate,
                                                const char *where) {
-    return (_Py_MakeRecCheck(tstate) && _Py_CheckRecursiveCall(tstate, where));
+    return (_Py_ReachedRecursionLimit(tstate) && _Py_CheckRecursiveCall(tstate, where));
 }
 
 static inline int _Py_EnterRecursiveCall(const char *where) {
@@ -246,8 +246,6 @@ static inline void _Py_LeaveRecursiveCallTstate(PyThreadState *tstate) {
 
 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,
index c9e918bceda9fc7c0b8596c8e81d7c7653ddae96..ca6819d2cd447304afa53f539a29845a3e1b7df2 100644 (file)
@@ -316,15 +316,20 @@ static uintptr_t return_pointer_as_int(char* p) {
 
 static inline uintptr_t
 _Py_get_machine_stack_pointer(void) {
-#if _Py__has_builtin(__builtin_frame_address) || defined(__GNUC__)
-    return (uintptr_t)__builtin_frame_address(0);
-#elif defined(_MSC_VER)
-    return (uintptr_t)_AddressOfReturnAddress();
+    uintptr_t result;
+#if defined(_M_ARM64)
+    result = __getReg(31);
+#elif defined(_M_X64) || defined(_M_IX86)
+    result = (uintptr_t)_AddressOfReturnAddress();
+#elif defined(__aarch64__)
+    __asm__ ("mov %0, sp" : "=r" (result));
+#elif defined(__x86_64__)
+    __asm__("{movq %%rsp, %0" : "=r" (result));
 #else
     char here;
-    /* Avoid compiler warning about returning stack address */
-    return return_pointer_as_int(&here);
+    result = (uintptr_t)&here;
 #endif
+    return result;
 }
 
 static inline intptr_t
index 2a544edc431e6b1396918061389eca1e7dc18a35..66dd7cd843b04fc6f2452c8053951744fb99d4d5 100644 (file)
@@ -46,7 +46,8 @@ extern PyObject * _Py_CompileStringObjectWithModule(
  * stack consumption of PyEval_EvalDefault */
 #if (defined(Py_DEBUG) \
      || defined(_Py_ADDRESS_SANITIZER) \
-     || defined(_Py_THREAD_SANITIZER))
+     || defined(_Py_THREAD_SANITIZER)) \
+     || defined(_Py_UNDEFINED_BEHAVIOR_SANITIZER)
 #  define _PyOS_LOG2_STACK_MARGIN 12
 #else
 #  define _PyOS_LOG2_STACK_MARGIN 11
index 10dca684accee3c6042aa1f461de866a0ffe5609..cddf52d569bcd311f232080567514d67614fcc5b 100644 (file)
@@ -861,7 +861,7 @@ class ElementDeclHandlerTest(unittest.TestCase):
     def test_deeply_nested_content_model(self):
         # This should raise a RecursionError and not crash.
         # See https://github.com/python/cpython/issues/145986.
-        N = 500_000
+        N = 800_000
         data = (
             b'<!DOCTYPE root [\n<!ELEMENT root '
             + b'(a, ' * N + b'a' + b')' * N
index 5661200e74d0a55bce71bb4b5c2a2e29f8e313b8..5dbb116ded165ec9ee1ff7b8100fb3ffa9e291fc 100644 (file)
@@ -49,20 +49,6 @@ _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count)
 #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)
@@ -278,7 +264,7 @@ PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate)
 
 
 /* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall()
-   if the stack pointer is between the stack base and c_stack_hard_limit. */
+   if the stack pointer is beyond c_stack_soft_limit. */
 int
 _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
 {
@@ -287,16 +273,21 @@ _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) {
-        /* Overflowing while handling an overflow. Give up. */
+        if (here_addr < _tstate->c_stack_hard_limit - _PyOS_STACK_MARGIN_BYTES) {
+            // Far out of bounds -- Assume stack switching has occurred
+            return 0;
+        }
         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) {
-        /* Overflowing while handling an overflow. Give up. */
+        if (here_addr > _tstate->c_stack_hard_limit + _PyOS_STACK_MARGIN_BYTES) {
+            // Far out of bounds -- Assume stack switching has occurred
+            return 0;
+        }
         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);
@@ -1146,19 +1137,6 @@ _PyEval_GetIter(_PyStackRef iterable, _PyStackRef *index_or_null, int yield_from
     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
@@ -1169,7 +1147,7 @@ _Py_ReachedRecursionLimit(PyThreadState *tstate)  {
  * (prior to GCC 9, 40% performance drop), so we have to selectively disable
  * it.
  */
-#define DONT_SLP_VECTORIZE __attribute__((optimize ("no-tree-slp-vectorize")))
+#define DONT_SLP_VECTORIZE __attribute__((optimize ("no-tree-slp-vectorize", "no-omit-frame-pointer")))
 #else
 #define DONT_SLP_VECTORIZE
 #endif
index 67dd88f510040eb8057fa9a8b294a4e0b0998266..ade8b8d36e789d429b4669a39f9da4ae83a9d747 100644 (file)
@@ -33,7 +33,7 @@
 
 #include "pycore_jit.h"
 
-// Memory management stuff: ////////////////////////////////////////////////////
+// Memory management stuff: ///////////////////////////////////////////////////
 
 #ifndef MS_WINDOWS
     #include <sys/mman.h>