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/tests/stack_limit/stack_limit_010.phpt b/Zend/tests/stack_limit/stack_limit_010.phpt index 85736423e41e..31fbc95d731f 100644 --- a/Zend/tests/stack_limit/stack_limit_010.phpt +++ b/Zend/tests/stack_limit/stack_limit_010.phpt @@ -30,13 +30,24 @@ $expectedMaxSize = match(php_uname('s')) { 'SunOS' => preg_match('/(omnios|illumos|smartos|oi-|openindiana|joyent)/i', php_uname('v')) ? 10 * 1024 * 1024 : 8 * 1024 * 1024, + 'OS400' => 0xFFFF_000, // defaults to maximum possible size 'Windows NT' => 67108864 - 4*4096, // Set by sapi/cli/config.w32 }; printf("Expected max_size: 0x%x\n", $expectedMaxSize); printf("Actual max_size: %s\n", $stack['max_size']); -var_dump($stack['max_size'] === sprintf('0x%x', $expectedMaxSize)); +// AIX/PASE returns a more exact number which is variable depending on i.e. +// OS version or other factors, due to stuff at the top of the stack. Let's +// just round to page size to try. +if (PHP_OS_FAMILY == "AIX") { + $pageSize = 4096; + $maxSize = hexdec($stack['max_size']); + $maxSize = ($maxSize + $pageSize - 1) & ~($pageSize - 1); + var_dump(sprintf('0x%x', $maxSize) === sprintf('0x%x', $expectedMaxSize)); +} else { + var_dump($stack['max_size'] === sprintf('0x%x', $expectedMaxSize)); +} ?> --EXPECTF-- diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c index aa23c2e3e2fa..40f0c7e549c2 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,79 @@ 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 = {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 + * suspended threads... + */ + int regsz = 0; + + if (pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_ALL, &thread_info, + sizeof(thread_info), NULL, ®sz)) { + 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 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 + * + * 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 +897,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; } 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; }