Skip to content

Commit 394db66

Browse files
authored
[3.14] GH-139914: Handle stack growth direction on HPPA (GH-140028) (#141404)
* [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
1 parent 73c9870 commit 394db66

File tree

10 files changed

+94
-8
lines changed

10 files changed

+94
-8
lines changed

Include/internal/pycore_ceval.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,11 @@ extern void _PyEval_DeactivateOpCache(void);
201201
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
202202
uintptr_t here_addr = _Py_get_machine_stack_pointer();
203203
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
204+
#if _Py_STACK_GROWS_DOWN
204205
return here_addr < _tstate->c_stack_soft_limit;
206+
#else
207+
return here_addr > _tstate->c_stack_soft_limit;
208+
#endif
205209
}
206210

207211
// Export for '_json' shared extension, used via _Py_EnterRecursiveCall()
@@ -233,7 +237,11 @@ static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
233237
uintptr_t here_addr = _Py_get_machine_stack_pointer();
234238
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
235239
assert(_tstate->c_stack_hard_limit != 0);
240+
#if _Py_STACK_GROWS_DOWN
236241
return here_addr <= _tstate->c_stack_soft_limit;
242+
#else
243+
return here_addr >= _tstate->c_stack_soft_limit;
244+
#endif
237245
}
238246

239247
static inline void _Py_LeaveRecursiveCall(void) {

Include/internal/pycore_pystate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,11 @@ _Py_RecursionLimit_GetMargin(PyThreadState *tstate)
326326
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
327327
assert(_tstate->c_stack_hard_limit != 0);
328328
intptr_t here_addr = _Py_get_machine_stack_pointer();
329+
#if _Py_STACK_GROWS_DOWN
329330
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, here_addr - (intptr_t)_tstate->c_stack_soft_limit, _PyOS_STACK_MARGIN_SHIFT);
331+
#else
332+
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, (intptr_t)_tstate->c_stack_soft_limit - here_addr, _PyOS_STACK_MARGIN_SHIFT);
333+
#endif
330334
}
331335

332336
#ifdef __cplusplus

Include/pyport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,4 +701,10 @@ extern "C" {
701701
#endif
702702

703703

704+
// Assume the stack grows down unless specified otherwise
705+
#ifndef _Py_STACK_GROWS_DOWN
706+
# define _Py_STACK_GROWS_DOWN 1
707+
#endif
708+
709+
704710
#endif /* Py_PYPORT_H */

Lib/test/test_call.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,9 +1048,14 @@ def get_sp():
10481048

10491049
this_sp = _testinternalcapi.get_stack_pointer()
10501050
lower_sp = _testcapi.pyobject_vectorcall(get_sp, (), ())
1051-
self.assertLess(lower_sp, this_sp)
1051+
if _testcapi._Py_STACK_GROWS_DOWN:
1052+
self.assertLess(lower_sp, this_sp)
1053+
safe_margin = this_sp - lower_sp
1054+
else:
1055+
self.assertLess(this_sp, lower_sp)
1056+
safe_margin = lower_sp - this_sp
10521057
# Add an (arbitrary) extra 20% for safety
1053-
safe_margin = (this_sp - lower_sp) * 6 / 5
1058+
safe_margin *= 6 / 5
10541059
self.assertLess(safe_margin, _testinternalcapi.get_stack_margin())
10551060

10561061
@skip_on_s390x
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Restore support for HP PA-RISC, which has an upwards-growing stack.

Modules/_testcapimodule.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3337,6 +3337,10 @@ PyInit__testcapi(void)
33373337
PyModule_AddObject(m, "INT64_MAX", PyLong_FromInt64(INT64_MAX));
33383338
PyModule_AddObject(m, "UINT64_MAX", PyLong_FromUInt64(UINT64_MAX));
33393339

3340+
if (PyModule_AddIntMacro(m, _Py_STACK_GROWS_DOWN)) {
3341+
return NULL;
3342+
}
3343+
33403344
if (PyModule_AddIntMacro(m, Py_single_input)) {
33413345
return NULL;
33423346
}

Python/ceval.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -333,21 +333,33 @@ _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count)
333333
{
334334
uintptr_t here_addr = _Py_get_machine_stack_pointer();
335335
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
336+
#if _Py_STACK_GROWS_DOWN
336337
if (here_addr > _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES) {
338+
#else
339+
if (here_addr <= _tstate->c_stack_soft_limit - margin_count * _PyOS_STACK_MARGIN_BYTES) {
340+
#endif
337341
return 0;
338342
}
339343
if (_tstate->c_stack_hard_limit == 0) {
340344
_Py_InitializeRecursionLimits(tstate);
341345
}
346+
#if _Py_STACK_GROWS_DOWN
342347
return here_addr <= _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES;
348+
#else
349+
return here_addr > _tstate->c_stack_soft_limit - margin_count * _PyOS_STACK_MARGIN_BYTES;
350+
#endif
343351
}
344352

345353
void
346354
_Py_EnterRecursiveCallUnchecked(PyThreadState *tstate)
347355
{
348356
uintptr_t here_addr = _Py_get_machine_stack_pointer();
349357
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
358+
#if _Py_STACK_GROWS_DOWN
350359
if (here_addr < _tstate->c_stack_hard_limit) {
360+
#else
361+
if (here_addr > _tstate->c_stack_hard_limit) {
362+
#endif
351363
Py_FatalError("Unchecked stack overflow.");
352364
}
353365
}
@@ -455,16 +467,28 @@ _Py_InitializeRecursionLimits(PyThreadState *tstate)
455467
}
456468
if (err == 0) {
457469
uintptr_t base = ((uintptr_t)stack_addr) + guard_size;
458-
_tstate->c_stack_top = base + stack_size;
459-
#ifdef _Py_THREAD_SANITIZER
470+
uintptr_t top = base + stack_size;
471+
# ifdef _Py_THREAD_SANITIZER
460472
// Thread sanitizer crashes if we use a bit more than half the stack.
461-
_tstate->c_stack_soft_limit = base + (stack_size / 2);
462-
#else
463-
_tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2;
464-
#endif
473+
# if _Py_STACK_GROWS_DOWN
474+
base += stack_size / 2;
475+
# else
476+
top -= stack_size / 2;
477+
# endif
478+
# endif
479+
# if _Py_STACK_GROWS_DOWN
480+
_tstate->c_stack_top = top;
465481
_tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES;
482+
_tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2;
466483
assert(_tstate->c_stack_soft_limit < here_addr);
467484
assert(here_addr < _tstate->c_stack_top);
485+
# else
486+
_tstate->c_stack_top = base;
487+
_tstate->c_stack_hard_limit = top - _PyOS_STACK_MARGIN_BYTES;
488+
_tstate->c_stack_soft_limit = top - _PyOS_STACK_MARGIN_BYTES * 2;
489+
assert(here_addr > base);
490+
assert(here_addr < _tstate->c_stack_soft_limit);
491+
# endif
468492
return;
469493
}
470494
# endif
@@ -483,9 +507,15 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
483507
uintptr_t here_addr = _Py_get_machine_stack_pointer();
484508
assert(_tstate->c_stack_soft_limit != 0);
485509
assert(_tstate->c_stack_hard_limit != 0);
510+
#if _Py_STACK_GROWS_DOWN
486511
if (here_addr < _tstate->c_stack_hard_limit) {
487512
/* Overflowing while handling an overflow. Give up. */
488513
int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
514+
#else
515+
if (here_addr > _tstate->c_stack_hard_limit) {
516+
/* Overflowing while handling an overflow. Give up. */
517+
int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
518+
#endif
489519
char buffer[80];
490520
snprintf(buffer, 80, "Unrecoverable stack overflow (used %d kB)%s", kbytes_used, where);
491521
Py_FatalError(buffer);
@@ -494,7 +524,11 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
494524
return 0;
495525
}
496526
else {
527+
#if _Py_STACK_GROWS_DOWN
497528
int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
529+
#else
530+
int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
531+
#endif
498532
tstate->recursion_headroom++;
499533
_PyErr_Format(tstate, PyExc_RecursionError,
500534
"Stack overflow (used %d kB)%s",

configure

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,14 @@ if test x$MULTIARCH != x; then
12021202
fi
12031203
AC_SUBST([MULTIARCH_CPPFLAGS])
12041204

1205+
# Guess C stack direction
1206+
AS_CASE([$host],
1207+
[hppa*], [_Py_STACK_GROWS_DOWN=0],
1208+
[_Py_STACK_GROWS_DOWN=1])
1209+
AC_DEFINE_UNQUOTED([_Py_STACK_GROWS_DOWN], [$_Py_STACK_GROWS_DOWN],
1210+
[Define to 1 if the machine stack grows down (default); 0 if it grows up.])
1211+
AC_SUBST([_Py_STACK_GROWS_DOWN])
1212+
12051213
dnl Support tiers according to https://peps.python.org/pep-0011/
12061214
dnl
12071215
dnl NOTE: Windows support tiers are defined in PC/pyconfig.h.

pyconfig.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,6 +2023,9 @@
20232023
/* HACL* library can compile SIMD256 implementations */
20242024
#undef _Py_HACL_CAN_COMPILE_VEC256
20252025

2026+
/* Define to 1 if the machine stack grows down (default); 0 if it grows up. */
2027+
#undef _Py_STACK_GROWS_DOWN
2028+
20262029
/* Define to force use of thread-safe errno, h_errno, and other functions */
20272030
#undef _REENTRANT
20282031

0 commit comments

Comments
 (0)