]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-126910: Revert "Make `_Py_get_machine_stack_pointer` return the stack pointer...
authorPetr Viktorin <encukou@gmail.com>
Thu, 2 Apr 2026 14:53:09 +0000 (16:53 +0200)
committerGitHub <noreply@github.com>
Thu, 2 Apr 2026 14:53:09 +0000 (16:53 +0200)
Revert "GH-126910: Make `_Py_get_machine_stack_pointer` return the stack pointer (#147945)"

This reverts commit 255026d9eea81719214c8e807d23df55b5f39b54,
which broke a tier-1 buildbot.

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

index c86cd58e295e5372f9040b64f92042ac62098bc8..9fd3be744049078d0ce92c3eced9e1beee9a8764 100644 (file)
@@ -211,16 +211,16 @@ extern void _PyEval_DeactivateOpCache(void);
 
 /* --- _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
 }
 
@@ -235,7 +235,7 @@ PyAPI_FUNC(int) _Py_CheckRecursiveCallPy(
 
 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) {
@@ -249,6 +249,8 @@ 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 054360d69e6fae47ca3f860a1135c3e3d03ed3aa..189a8dde9f09ed61f0d26a789be90578e73269c6 100644 (file)
@@ -306,23 +306,23 @@ _Py_AssertHoldsTstateFunc(const char *func)
 #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
index 66dd7cd843b04fc6f2452c8053951744fb99d4d5..2a544edc431e6b1396918061389eca1e7dc18a35 100644 (file)
@@ -46,8 +46,7 @@ extern PyObject * _Py_CompileStringObjectWithModule(
  * 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
index ee90711c202482e8460ead7dc87e5b53d19ef93e..f7bb5d513b9ae668f44c322fe7ed19d7352141b2 100644 (file)
@@ -598,11 +598,6 @@ extern "C" {
 #      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
index 0361d9f3da906959dee51832c902a546e78265ce..cace780f79f5152f57a0dc7db6a7a2c19e61ad10 100644 (file)
@@ -707,7 +707,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 = 800_000
+        N = 500_000
         data = (
             b'<!DOCTYPE root [\n<!ELEMENT root '
             + b'(a, ' * N + b'a' + b')' * N
index d4bc45f9c9130e722344f386849d850dc59a5a0b..bf550f2da3662e0bd9f4a190e9e558021115ca39 100644 (file)
@@ -49,6 +49,20 @@ _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)
@@ -264,7 +278,7 @@ PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate)
 
 
 /* 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)
 {
@@ -273,21 +287,16 @@ _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);
@@ -1192,6 +1201,19 @@ _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
index d3a40ee6ff64d5faf40671c5fa6079f2769bef5e..4990c743224d3ca5f75de5bc2f327c813f34004e 100644 (file)
@@ -734,7 +734,7 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
     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.