From 5a6f841b7c8f6f900b42a661534ec3097e89c9c4 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Tue, 28 Jul 2026 02:47:38 -0300 Subject: [PATCH 1/6] zend_call_stack.c: AIX backend Uses pthread_getthrds_np (so requires pthread linked, by default on ZTS, needs to be added for NTS). The stack size part is weird, but works in testing. An alternative approach is using procfs, but I didn't bother with this due to it not working on PASE (and the pthread approach works for the main thread too). --- Zend/Zend.m4 | 1 + Zend/zend_call_stack.c | 65 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 index 5e69a97db197..41da8344bf1f 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -148,6 +148,7 @@ AC_CHECK_FUNCS(m4_normalize([ pthread_attr_getstack pthread_get_stackaddr_np pthread_getattr_np + pthread_getthrds_np pthread_stackseg_np strnlen ])) diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c index aa23c2e3e2fa..d6fe28dee2b5 100644 --- a/Zend/zend_call_stack.c +++ b/Zend/zend_call_stack.c @@ -36,7 +36,8 @@ #endif /* ZEND_WIN32 */ #if (defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)) || \ defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || \ - defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun) + defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun) || \ + defined(_AIX) # include #endif #if defined(__FreeBSD__) || defined(__DragonFly__) @@ -788,6 +789,64 @@ static bool zend_call_stack_get_solaris(zend_call_stack *stack) } #endif /* defined(__sun) */ +#if defined(_AIX) +static bool zend_call_stack_get_aix_pthread(zend_call_stack *stack) +{ +#ifdef HAVE_PTHREAD_GETTHRDS_NP + pthread_t pt = pthread_self(); + struct __pthrdsinfo thread_info; + /* This buffer needs to exist sadly */ + char reg[256]; + int regsz = sizeof(reg); + + if (pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_ALL, &thread_info, + sizeof(thread_info), ®, ®sz)) { + return false; + } + + /* + * The top of the stack (stackend) is not page aligned, there's some + * internal stuff above it. Thankfully, we don't need page alignment. + * + * The size is a little weird. The stacksize field is smaller than + * subtracting the bottom (stackaddr) from the top; it's about 0x888 + * to 0x1888 above stackaddr. + * + * A somewhat crude diagram is available here: + * https://www.ibm.com/docs/en/aix/7.2.0?topic=tuning-thread-environment-variables + * + * pthread->pt_stk.st_limit is __pi_stackend, + * above that is internal pthread junk close to the end of page + * pthread->pt_stk.st_base is __pi_stackaddr, + * below that is the red zone + */ + stack->base = thread_info.__pi_stackend; + stack->max_size = thread_info.__pi_stackend - thread_info.__pi_stackaddr; + return true; +#else + /* pthread likely not linked in; default NTS build behaviour */ + return false; +#endif +} + +static bool zend_call_stack_get_aix(zend_call_stack *stack) +{ + /* + * While we could use /proc on AIX (and the implementation basically + * like the Solaris one, as the procfs is similar), it doesn't work on + * PASE. The pthread API works even on the main thread, so we should + * use it when we have pthread linked (always with ZTS, maybe not with + * NTS builds). + */ + return zend_call_stack_get_aix_pthread(stack); +} +#else +static bool zend_call_stack_get_aix(zend_call_stack *stack) +{ + return false; +} +#endif /* defined(_AIX) */ + /** Get the stack information for the calling thread */ ZEND_API bool zend_call_stack_get(zend_call_stack *stack) { @@ -823,6 +882,10 @@ ZEND_API bool zend_call_stack_get(zend_call_stack *stack) return true; } + if (zend_call_stack_get_aix(stack)) { + return true; + } + return false; } From 299074213084f1d12d5cc207ec4fe50f937dbe5a Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Tue, 28 Jul 2026 14:39:44 -0300 Subject: [PATCH 2/6] We can have zero length register size ...and pass a NULL register buffer, since this is only used with suspended threads. something that'll never happen if we invoke it on ourself. We still need to pass the size though. --- Zend/zend_call_stack.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c index d6fe28dee2b5..ad72171aa3e0 100644 --- a/Zend/zend_call_stack.c +++ b/Zend/zend_call_stack.c @@ -795,12 +795,15 @@ static bool zend_call_stack_get_aix_pthread(zend_call_stack *stack) #ifdef HAVE_PTHREAD_GETTHRDS_NP pthread_t pt = pthread_self(); struct __pthrdsinfo thread_info; - /* This buffer needs to exist sadly */ - char reg[256]; - int regsz = sizeof(reg); + /* + * We don't need the register buffer since we only call the function + * on our own thread, and since the register buffer is only used for + * suspended threads... + */ + int regsz = 0; if (pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_ALL, &thread_info, - sizeof(thread_info), ®, ®sz)) { + sizeof(thread_info), NULL, ®sz)) { return false; } From 7d1b19380c99af86e3b2bc4c902c44dab94d922c Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Wed, 29 Jul 2026 11:23:03 -0300 Subject: [PATCH 3/6] More safeties here Zero out the pthread struct, check if the pointers are nil, add extra comment about stackaddr --- Zend/zend_call_stack.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c index ad72171aa3e0..fe1491d07d98 100644 --- a/Zend/zend_call_stack.c +++ b/Zend/zend_call_stack.c @@ -794,7 +794,7 @@ static bool zend_call_stack_get_aix_pthread(zend_call_stack *stack) { #ifdef HAVE_PTHREAD_GETTHRDS_NP pthread_t pt = pthread_self(); - struct __pthrdsinfo thread_info; + struct __pthrdsinfo thread_info = {0}; /* * We don't need the register buffer since we only call the function * on our own thread, and since the register buffer is only used for @@ -807,13 +807,22 @@ static bool zend_call_stack_get_aix_pthread(zend_call_stack *stack) return false; } + /* + * These can be null in rare situations, allegedly with user-provided + * stacks with pthread (according to OpenJDK) + */ + if (!(thread_info.__pi_stackend && thread_info.__pi_stackaddr)) { + return false; + } + /* * The top of the stack (stackend) is not page aligned, there's some * internal stuff above it. Thankfully, we don't need page alignment. * * The size is a little weird. The stacksize field is smaller than * subtracting the bottom (stackaddr) from the top; it's about 0x888 - * to 0x1888 above stackaddr. + * to 0x1888 above stackaddr. I'm assuming it rounds the bottom of the + * stack to page alignment? * * A somewhat crude diagram is available here: * https://www.ibm.com/docs/en/aix/7.2.0?topic=tuning-thread-environment-variables From e0975ca90573beca478a382f4ccd2f876395258b Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Thu, 30 Jul 2026 13:23:11 -0300 Subject: [PATCH 4/6] Add zend_call_stack_default_size for AIX --- Zend/zend_call_stack.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Zend/zend_call_stack.h b/Zend/zend_call_stack.h index c0b84334239d..f70b9edae302 100644 --- a/Zend/zend_call_stack.h +++ b/Zend/zend_call_stack.h @@ -101,6 +101,32 @@ static inline size_t zend_call_stack_default_size(void) #ifdef __sun return 8 * 4096; #endif +#ifdef _AIX + /* + * default pthread stack limit is 96 KB on 32-bit, 192 KB on 64-bit + * https://www.ibm.com/docs/en/aix/7.1.0?topic=programming-threads-library-options + */ +#ifdef HAVE_PTHREAD_GETTHRDS_NP /* if we have pthread linked (not in libc) */ + if (pthread_self() != 1) { +#ifdef __64BIT__ + return 192 * 1024; +#else + return 96 * 1024; +#endif + } +#endif + /* + * default AIX ulimit -s value is allegedly 32 MB, default values per: + * https://www.ibm.com/docs/en/aix/7.1.0?topic=u-ulimit-command + * N.B.: the the file uses 512b blocks, but the command uses kilobytes + * https://www.ibm.com/support/pages/ibm-aix-security-ulimit-and-ulimit-d-output-differs-value-etcsecuritylimits + * + * note PASE uses an unlimited stack size by default, which is capped + * at the PowerPC segment size (256 MB) + */ + return 32 * 1024 * 1024; +#endif + return 2 * 1024 * 1024; } From 25674ce0720cfbeae7bcb4954e36794e3070fceb Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Thu, 30 Jul 2026 14:27:53 -0300 Subject: [PATCH 5/6] Update comment for size about main thread --- Zend/zend_call_stack.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c index fe1491d07d98..40f0c7e549c2 100644 --- a/Zend/zend_call_stack.c +++ b/Zend/zend_call_stack.c @@ -819,10 +819,13 @@ static bool zend_call_stack_get_aix_pthread(zend_call_stack *stack) * The top of the stack (stackend) is not page aligned, there's some * internal stuff above it. Thankfully, we don't need page alignment. * - * The size is a little weird. The stacksize field is smaller than - * subtracting the bottom (stackaddr) from the top; it's about 0x888 - * to 0x1888 above stackaddr. I'm assuming it rounds the bottom of the - * stack to page alignment? + * The size is a little weird. The stacksize field for child threads + * is smaller than subtracting the bottom (stackaddr) from the top; + * it's about 0x888 to 0x1888 above stackaddr. I'm assuming it rounds + * the bottom of the stack to page alignment? The main thread size is + * the same as end - addr, but it is variable between systems; also + * assuming there's stuff at the top of the stack that gets taken off, + * regardless of maximum declared size. * * A somewhat crude diagram is available here: * https://www.ibm.com/docs/en/aix/7.2.0?topic=tuning-thread-environment-variables From b143bd12724d7c424c3dd9e0994fbe50120d74cc Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 31 Jul 2026 14:52:58 -0300 Subject: [PATCH 6/6] Skip this test on AIX even with var set AIX adjusts the top address of the stack on the main thread in an unpredictable way, and this is used to calculate the size for the main thread. --- Zend/tests/stack_limit/stack_limit_010.phpt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Zend/tests/stack_limit/stack_limit_010.phpt b/Zend/tests/stack_limit/stack_limit_010.phpt index 85736423e41e..9c9295c65b69 100644 --- a/Zend/tests/stack_limit/stack_limit_010.phpt +++ b/Zend/tests/stack_limit/stack_limit_010.phpt @@ -4,6 +4,9 @@ Stack limit 010 - Check stack size detection against known defaults zend_test --SKIPIF--