]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] GH-139914: Handle stack growth direction on HPPA (GH-140028) (#141404)
authorStefano Rivera <stefano@rivera.za.net>
Sun, 23 Nov 2025 23:55:33 +0000 (15:55 -0800)
committerGitHub <noreply@github.com>
Sun, 23 Nov 2025 23:55:33 +0000 (15:55 -0800)
* [3.14] GH-139914: Handle stack growth direction on HPPA (GH-140028)

Adapted from a patch for Python 3.14 submitted to the Debian BTS by John David Anglin https://bugs.debian.org/1105111#20

* Forgot to update test_call

* WTF typo

Include/internal/pycore_ceval.h
Include/internal/pycore_pystate.h
Include/pyport.h
Lib/test/test_call.py
Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-13-54-19.gh-issue-139914.M-y_3E.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Python/ceval.c
configure
configure.ac
pyconfig.h.in

index a3d9e7bf87a7ba43296d9f4e44ec13117e9f949a..905cc0cf0509b84db3020af57dd36a7b5a5d918f 100644 (file)
@@ -201,7 +201,11 @@ extern void _PyEval_DeactivateOpCache(void);
 static inline int _Py_MakeRecCheck(PyThreadState *tstate)  {
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
+#if _Py_STACK_GROWS_DOWN
     return here_addr < _tstate->c_stack_soft_limit;
+#else
+    return here_addr > _tstate->c_stack_soft_limit;
+#endif
 }
 
 // Export for '_json' shared extension, used via _Py_EnterRecursiveCall()
@@ -233,7 +237,11 @@ static 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
 }
 
 static inline void _Py_LeaveRecursiveCall(void)  {
index ea3dfbd2eef9c1594e851b41864d0effef9db4d6..7f1b0b7523b20d9c3fce71dc9df73fde26a209cc 100644 (file)
@@ -326,7 +326,11 @@ _Py_RecursionLimit_GetMargin(PyThreadState *tstate)
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
     assert(_tstate->c_stack_hard_limit != 0);
     intptr_t here_addr = _Py_get_machine_stack_pointer();
+#if _Py_STACK_GROWS_DOWN
     return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, here_addr - (intptr_t)_tstate->c_stack_soft_limit, _PyOS_STACK_MARGIN_SHIFT);
+#else
+    return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, (intptr_t)_tstate->c_stack_soft_limit - here_addr, _PyOS_STACK_MARGIN_SHIFT);
+#endif
 }
 
 #ifdef __cplusplus
index 44894419f6231d89f166069a80f1548f4cf15b24..2c8567f2554d9b9e9d8e3c33b5392630e74a8f0d 100644 (file)
@@ -701,4 +701,10 @@ extern "C" {
 #endif
 
 
+// Assume the stack grows down unless specified otherwise
+#ifndef _Py_STACK_GROWS_DOWN
+#  define _Py_STACK_GROWS_DOWN 1
+#endif
+
+
 #endif /* Py_PYPORT_H */
index fcbef1ae86bed543eed6f74075d6d4f59e407086..b18f16a6efcf84a003e407818a60250c91aad3d6 100644 (file)
@@ -1048,9 +1048,14 @@ class TestRecursion(unittest.TestCase):
 
         this_sp = _testinternalcapi.get_stack_pointer()
         lower_sp = _testcapi.pyobject_vectorcall(get_sp, (), ())
-        self.assertLess(lower_sp, this_sp)
+        if _testcapi._Py_STACK_GROWS_DOWN:
+            self.assertLess(lower_sp, this_sp)
+            safe_margin = this_sp - lower_sp
+        else:
+            self.assertLess(this_sp, lower_sp)
+            safe_margin = lower_sp - this_sp
         # Add an (arbitrary) extra 20% for safety
-        safe_margin = (this_sp - lower_sp) * 6 / 5
+        safe_margin *= 6 / 5
         self.assertLess(safe_margin, _testinternalcapi.get_stack_margin())
 
     @skip_on_s390x
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-13-54-19.gh-issue-139914.M-y_3E.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-13-54-19.gh-issue-139914.M-y_3E.rst
new file mode 100644 (file)
index 0000000..7529108
--- /dev/null
@@ -0,0 +1 @@
+Restore support for HP PA-RISC, which has an upwards-growing stack.
index 2572df9719a703d6dae32ee7e8f662545b2b51cc..228fe4eff5812bda56441121e6980f956c352e84 100644 (file)
@@ -3337,6 +3337,10 @@ PyInit__testcapi(void)
     PyModule_AddObject(m, "INT64_MAX", PyLong_FromInt64(INT64_MAX));
     PyModule_AddObject(m, "UINT64_MAX", PyLong_FromUInt64(UINT64_MAX));
 
+    if (PyModule_AddIntMacro(m, _Py_STACK_GROWS_DOWN)) {
+        return NULL;
+    }
+
     if (PyModule_AddIntMacro(m, Py_single_input)) {
         return NULL;
     }
index 6ce20af62e72494fb8e2ead2c4b927586a5290f4..d37a98df418322258a916b806e70481b41869997 100644 (file)
@@ -333,13 +333,21 @@ _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count)
 {
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
+#if _Py_STACK_GROWS_DOWN
     if (here_addr > _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES) {
+#else
+    if (here_addr <= _tstate->c_stack_soft_limit - margin_count * _PyOS_STACK_MARGIN_BYTES) {
+#endif
         return 0;
     }
     if (_tstate->c_stack_hard_limit == 0) {
         _Py_InitializeRecursionLimits(tstate);
     }
+#if _Py_STACK_GROWS_DOWN
     return here_addr <= _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES;
+#else
+    return here_addr > _tstate->c_stack_soft_limit - margin_count * _PyOS_STACK_MARGIN_BYTES;
+#endif
 }
 
 void
@@ -347,7 +355,11 @@ _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.");
     }
 }
@@ -455,16 +467,28 @@ _Py_InitializeRecursionLimits(PyThreadState *tstate)
     }
     if (err == 0) {
         uintptr_t base = ((uintptr_t)stack_addr) + guard_size;
-        _tstate->c_stack_top = base + stack_size;
-#ifdef _Py_THREAD_SANITIZER
+        uintptr_t top = base + stack_size;
+#    ifdef _Py_THREAD_SANITIZER
         // Thread sanitizer crashes if we use a bit more than half the stack.
-        _tstate->c_stack_soft_limit = base + (stack_size / 2);
-#else
-        _tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2;
-#endif
+#      if _Py_STACK_GROWS_DOWN
+        base += stack_size / 2;
+#      else
+        top -= stack_size / 2;
+#      endif
+#    endif
+#    if _Py_STACK_GROWS_DOWN
+        _tstate->c_stack_top = top;
         _tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES;
+        _tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2;
         assert(_tstate->c_stack_soft_limit < here_addr);
         assert(here_addr < _tstate->c_stack_top);
+#    else
+        _tstate->c_stack_top = base;
+        _tstate->c_stack_hard_limit = top - _PyOS_STACK_MARGIN_BYTES;
+        _tstate->c_stack_soft_limit = top - _PyOS_STACK_MARGIN_BYTES * 2;
+        assert(here_addr > base);
+        assert(here_addr < _tstate->c_stack_soft_limit);
+#    endif
         return;
     }
 #  endif
@@ -483,9 +507,15 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     assert(_tstate->c_stack_soft_limit != 0);
     assert(_tstate->c_stack_hard_limit != 0);
+#if _Py_STACK_GROWS_DOWN
     if (here_addr < _tstate->c_stack_hard_limit) {
         /* Overflowing while handling an overflow. Give up. */
         int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
+#else
+    if (here_addr > _tstate->c_stack_hard_limit) {
+        /* Overflowing while handling an overflow. Give up. */
+        int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
+#endif
         char buffer[80];
         snprintf(buffer, 80, "Unrecoverable stack overflow (used %d kB)%s", kbytes_used, where);
         Py_FatalError(buffer);
@@ -494,7 +524,11 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
         return 0;
     }
     else {
+#if _Py_STACK_GROWS_DOWN
         int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
+#else
+        int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
+#endif
         tstate->recursion_headroom++;
         _PyErr_Format(tstate, PyExc_RecursionError,
                     "Stack overflow (used %d kB)%s",
index fb92ea2c61febdc2cfba1a2e0c24ef4bbec2d419..46eb0665bf4795a53c3322c2f5acf9437a41b0a5 100755 (executable)
--- a/configure
+++ b/configure
@@ -965,6 +965,7 @@ LDLIBRARY
 LIBRARY
 BUILDEXEEXT
 NO_AS_NEEDED
+_Py_STACK_GROWS_DOWN
 MULTIARCH_CPPFLAGS
 PLATFORM_TRIPLET
 MULTIARCH
@@ -7211,6 +7212,18 @@ if test x$MULTIARCH != x; then
 fi
 
 
+# Guess C stack direction
+case $host in #(
+  hppa*) :
+    _Py_STACK_GROWS_DOWN=0 ;; #(
+  *) :
+    _Py_STACK_GROWS_DOWN=1 ;;
+esac
+
+printf "%s\n" "#define _Py_STACK_GROWS_DOWN $_Py_STACK_GROWS_DOWN" >>confdefs.h
+
+
+
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PEP 11 support tier" >&5
 printf %s "checking for PEP 11 support tier... " >&6; }
 case $host/$ac_cv_cc_name in #(
index 0742ba7f6d1498e2283868d4439a03bb16b7ae72..bd4446e9488f7ba5e09b518caf7b71c01916ad54 100644 (file)
@@ -1202,6 +1202,14 @@ if test x$MULTIARCH != x; then
 fi
 AC_SUBST([MULTIARCH_CPPFLAGS])
 
+# Guess C stack direction
+AS_CASE([$host],
+  [hppa*], [_Py_STACK_GROWS_DOWN=0],
+  [_Py_STACK_GROWS_DOWN=1])
+AC_DEFINE_UNQUOTED([_Py_STACK_GROWS_DOWN], [$_Py_STACK_GROWS_DOWN],
+  [Define to 1 if the machine stack grows down (default); 0 if it grows up.])
+AC_SUBST([_Py_STACK_GROWS_DOWN])
+
 dnl Support tiers according to https://peps.python.org/pep-0011/
 dnl
 dnl NOTE: Windows support tiers are defined in PC/pyconfig.h.
index 0d6ad4465c0e934e13665ceadda79099cffc35a5..9502fcebf5d7802145dc43b53e7b0917fad6338c 100644 (file)
 /* HACL* library can compile SIMD256 implementations */
 #undef _Py_HACL_CAN_COMPILE_VEC256
 
+/* Define to 1 if the machine stack grows down (default); 0 if it grows up. */
+#undef _Py_STACK_GROWS_DOWN
+
 /* Define to force use of thread-safe errno, h_errno, and other functions */
 #undef _REENTRANT