From cd23701f2134ff52046f2f48ff1bd313dbd02c07 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 14 Apr 2023 12:15:27 +0200 Subject: [PATCH 1/2] Increase zend.reserved_stack_size minimum value in ASAN/MSAN builds --- Zend/tests/stack_limit/stack_limit_001.phpt | 1 - Zend/tests/stack_limit/stack_limit_002.phpt | 1 - Zend/tests/stack_limit/stack_limit_003.phpt | 2 +- Zend/tests/stack_limit/stack_limit_004.phpt | 4 ++-- Zend/tests/stack_limit/stack_limit_006.phpt | 1 - Zend/tests/stack_limit/stack_limit_007.phpt | 2 +- Zend/tests/stack_limit/stack_limit_008.phpt | 2 +- Zend/tests/stack_limit/stack_limit_009.phpt | 1 - Zend/tests/stack_limit/stack_limit_011.phpt | 19 ++++++++++------- Zend/tests/stack_limit/stack_limit_012.phpt | 2 +- Zend/tests/stack_limit/stack_limit_013.inc | 13 ++++++++++++ Zend/tests/stack_limit/stack_limit_014.inc | 13 ++++++++++++ Zend/tests/stack_limit/stack_limit_014.phpt | 23 +++++++++++++++++++++ Zend/zend.c | 6 ++++++ Zend/zend_compile.c | 7 ++++++- Zend/zend_execute.c | 7 ++++++- 16 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 Zend/tests/stack_limit/stack_limit_013.inc create mode 100644 Zend/tests/stack_limit/stack_limit_014.inc create mode 100644 Zend/tests/stack_limit/stack_limit_014.phpt diff --git a/Zend/tests/stack_limit/stack_limit_001.phpt b/Zend/tests/stack_limit/stack_limit_001.phpt index 2de5d28134ce7..1a3bb9319e31e 100644 --- a/Zend/tests/stack_limit/stack_limit_001.phpt +++ b/Zend/tests/stack_limit/stack_limit_001.phpt @@ -3,7 +3,6 @@ Stack limit 001 - Stack limit checks with max_allowed_stack_size detection --SKIPIF-- --EXTENSIONS-- zend_test diff --git a/Zend/tests/stack_limit/stack_limit_002.phpt b/Zend/tests/stack_limit/stack_limit_002.phpt index 53fc707ca6a5e..44fab205f2ff0 100644 --- a/Zend/tests/stack_limit/stack_limit_002.phpt +++ b/Zend/tests/stack_limit/stack_limit_002.phpt @@ -3,7 +3,6 @@ Stack limit 002 - Stack limit checks with max_allowed_stack_size detection (fibe --SKIPIF-- --EXTENSIONS-- zend_test diff --git a/Zend/tests/stack_limit/stack_limit_003.phpt b/Zend/tests/stack_limit/stack_limit_003.phpt index c3ea35bf84950..9956c761cb184 100644 --- a/Zend/tests/stack_limit/stack_limit_003.phpt +++ b/Zend/tests/stack_limit/stack_limit_003.phpt @@ -7,7 +7,7 @@ if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_ --EXTENSIONS-- zend_test --INI-- -zend.max_allowed_stack_size=256K +zend.max_allowed_stack_size=512K --FILE-- start(); $depth1 = $fiber->getReturn(); -ini_set('fiber.stack_size', '200K'); +ini_set('fiber.stack_size', '512K'); $fiber = new Fiber($callback); $fiber->start(); $depth2 = $fiber->getReturn(); diff --git a/Zend/tests/stack_limit/stack_limit_006.phpt b/Zend/tests/stack_limit/stack_limit_006.phpt index 639b2f8e66ced..48a958bee3a97 100644 --- a/Zend/tests/stack_limit/stack_limit_006.phpt +++ b/Zend/tests/stack_limit/stack_limit_006.phpt @@ -3,7 +3,6 @@ Stack limit 006 - env size affects __libc_stack_end --SKIPIF-- --EXTENSIONS-- zend_test diff --git a/Zend/tests/stack_limit/stack_limit_007.phpt b/Zend/tests/stack_limit/stack_limit_007.phpt index 55b11f36fe341..d34e244234c2c 100644 --- a/Zend/tests/stack_limit/stack_limit_007.phpt +++ b/Zend/tests/stack_limit/stack_limit_007.phpt @@ -7,7 +7,7 @@ if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_ --EXTENSIONS-- zend_test --INI-- -zend.max_allowed_stack_size=256K +zend.max_allowed_stack_size=512K --FILE-- --EXTENSIONS-- zend_test diff --git a/Zend/tests/stack_limit/stack_limit_011.phpt b/Zend/tests/stack_limit/stack_limit_011.phpt index f04ad65427120..762f67184f6a0 100644 --- a/Zend/tests/stack_limit/stack_limit_011.phpt +++ b/Zend/tests/stack_limit/stack_limit_011.phpt @@ -7,24 +7,27 @@ if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_ --EXTENSIONS-- zend_test --INI-- -zend.max_allowed_stack_size=256K +zend.max_allowed_stack_size=512K --FILE-- +--EXTENSIONS-- +zend_test +--INI-- +; The test may use a large amount of memory on systems with a large stack limit +memory_limit=1G +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +%S%rMaximum call stack size of [0-9]+ bytes reached|Allowed memory size of [0-9]+ bytes exhausted%r%s diff --git a/Zend/zend.c b/Zend/zend.c index 49b63e4bccccd..73e7eac0ff1a8 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -214,6 +214,12 @@ static ZEND_INI_MH(OnUpdateReservedStackSize) /* {{{ */ zend_ulong min = 32*1024; #endif +#ifdef __SANITIZE_ADDRESS__ + /* AddressSanitizer and MemorySanitizer use more stack due to + * instrumentation */ + min *= 10; +#endif + if (size == 0) { size = min; } else if (size < min) { diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 140229706883b..79a65d2d5aded 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -103,9 +103,14 @@ static void zend_compile_assign(znode *result, zend_ast *ast); #ifdef ZEND_CHECK_STACK_LIMIT zend_never_inline static void zend_stack_limit_error(void) { + size_t max_stack_size = 0; + if ((uintptr_t) EG(stack_base) > (uintptr_t) EG(stack_limit)) { + max_stack_size = (size_t) ((uintptr_t) EG(stack_base) - (uintptr_t) EG(stack_limit)); + } + zend_error_noreturn(E_COMPILE_ERROR, "Maximum call stack size of %zu bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached during compilation. Try splitting expression", - (size_t) ((uintptr_t) EG(stack_base) - (uintptr_t) EG(stack_limit))); + max_stack_size); } static void zend_check_stack_limit(void) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index b0980c8d9f03e..b92c4c1174cdc 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2460,8 +2460,13 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_new_element_for_s #ifdef ZEND_CHECK_STACK_LIMIT static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_call_stack_size_error(void) { + size_t max_stack_size = 0; + if ((uintptr_t) EG(stack_base) > (uintptr_t) EG(stack_limit)) { + max_stack_size = (size_t) ((uintptr_t) EG(stack_base) - (uintptr_t) EG(stack_limit)); + } + zend_throw_error(NULL, "Maximum call stack size of %zu bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?", - (size_t) ((uintptr_t) EG(stack_base) - (uintptr_t) EG(stack_limit))); + max_stack_size); } #endif /* ZEND_CHECK_STACK_LIMIT */ From 153b09f9ee15ec552e93fdda875518ebc3fc1b86 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 2 Jul 2024 19:57:06 +0200 Subject: [PATCH 2/2] Fix stack limit with ASAN Local variables may not be stored on the real stack --- Zend/Zend.m4 | 15 +++++++++++++-- Zend/tests/stack_limit/stack_limit_014.phpt | 2 +- Zend/zend_call_stack.c | 6 +++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 index 4ec1b5518e723..85622ebb98e98 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -216,15 +216,26 @@ AC_DEFUN([ZEND_CHECK_STACK_DIRECTION], int (*volatile f)(uintptr_t); int stack_grows_downwards(uintptr_t arg) { +#if defined(__has_builtin) && __has_builtin(__builtin_frame_address) + uintptr_t addr = (uintptr_t)__builtin_frame_address(0); +#else int local; - return (uintptr_t)&local < arg; + uintptr_t addr = (uintptr_t)&local; +#endif + + return addr < arg; } int main(void) { +#if defined(__has_builtin) && __has_builtin(__builtin_frame_address) + uintptr_t addr = (uintptr_t)__builtin_frame_address(0); +#else int local; + uintptr_t addr = (uintptr_t)&local; +#endif f = stack_grows_downwards; - return f((uintptr_t)&local) ? 0 : 1; + return f(addr) ? 0 : 1; }])], [php_cv_have_stack_limit=yes], [php_cv_have_stack_limit=no], diff --git a/Zend/tests/stack_limit/stack_limit_014.phpt b/Zend/tests/stack_limit/stack_limit_014.phpt index 3b27191366bc7..9ccf3c0372926 100644 --- a/Zend/tests/stack_limit/stack_limit_014.phpt +++ b/Zend/tests/stack_limit/stack_limit_014.phpt @@ -20,4 +20,4 @@ try { ?> --EXPECTF-- -%S%rMaximum call stack size of [0-9]+ bytes reached|Allowed memory size of [0-9]+ bytes exhausted%r%s +%S%rMaximum call stack size of [0-9]+ bytes \(zend\.max_allowed_stack_size - zend\.reserved_stack_size\) reached|Allowed memory size of [0-9]+ bytes exhausted%r%s diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c index a815801c0fe5f..9edb4f2958943 100644 --- a/Zend/zend_call_stack.c +++ b/Zend/zend_call_stack.c @@ -176,7 +176,7 @@ static bool zend_call_stack_get_linux_proc_maps(zend_call_stack *stack) { FILE *f; char buffer[4096]; - uintptr_t addr_on_stack = (uintptr_t)&buffer; + uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); uintptr_t start, end, prev_end = 0; size_t max_size; bool found = false; @@ -610,7 +610,7 @@ static bool zend_call_stack_get_netbsd_vm(zend_call_stack *stack, void **ptr) struct kinfo_vmentry *entry; size_t len, max_size; char buffer[4096]; - uintptr_t addr_on_stack = (uintptr_t)&buffer; + uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); int mib[5] = { CTL_VM, VM_PROC, VM_PROC_MAP, getpid(), sizeof(struct kinfo_vmentry) }; bool found = false; struct rlimit rlim; @@ -691,7 +691,7 @@ static bool zend_call_stack_get_solaris_pthread(zend_call_stack *stack) static bool zend_call_stack_get_solaris_proc_maps(zend_call_stack *stack) { char buffer[4096]; - uintptr_t addr_on_stack = (uintptr_t)&buffer; + uintptr_t addr_on_stack = (uintptr_t) zend_call_stack_position(); bool found = false, r = false; struct ps_prochandle *proc; prmap_t *map, *orig;