Fix stack limit on ASAN/MSAN (#14771)

Increase the reserved stack size in ASAN builds, as instrumentation use more stack.
Increase the max allowed stack size in some tests, and enable these tests under ASAN.
Use __builtin_frame_address(0), instead of some stack variable, when we need a stack address, as ASAN may store local variables outside of the real stack.
This commit is contained in:
Arnaud Le Blanc 2024-07-03 19:23:34 +02:00 committed by GitHub
parent e63e1afd84
commit 0bd260218b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 101 additions and 26 deletions

View File

@ -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],

View File

@ -3,7 +3,6 @@ Stack limit 001 - Stack limit checks with max_allowed_stack_size detection
--SKIPIF--
<?php
if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_call_stack_get() is not available");
if (getenv('SKIP_MSAN')) die("skip msan requires a considerably higher zend.reserved_stack_size due to instrumentation");
?>
--EXTENSIONS--
zend_test

View File

@ -3,7 +3,6 @@ Stack limit 002 - Stack limit checks with max_allowed_stack_size detection (fibe
--SKIPIF--
<?php
if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_call_stack_get() is not available");
if (getenv('SKIP_MSAN')) die("skip msan requires a considerably higher zend.reserved_stack_size due to instrumentation");
?>
--EXTENSIONS--
zend_test

View File

@ -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--
<?php

View File

@ -27,12 +27,12 @@ $callback = function (): int {
throw new \Exception();
};
ini_set('fiber.stack_size', '400K');
ini_set('fiber.stack_size', '1M');
$fiber = new Fiber($callback);
$fiber->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();

View File

@ -3,7 +3,6 @@ Stack limit 006 - env size affects __libc_stack_end
--SKIPIF--
<?php
if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_call_stack_get() is not available");
if (getenv('SKIP_MSAN')) die("skip msan requires a considerably higher zend.reserved_stack_size due to instrumentation");
?>
--EXTENSIONS--
zend_test

View File

@ -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--
<?php

View File

@ -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--
<?php

View File

@ -3,7 +3,6 @@ Stack limit 009 - Check that we can actually use all the stack
--SKIPIF--
<?php
if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_call_stack_get() is not available");
if (getenv('SKIP_MSAN')) die("skip msan requires a considerably higher zend.reserved_stack_size due to instrumentation");
?>
--EXTENSIONS--
zend_test

View File

@ -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--
<?php
var_dump(zend_test_zend_call_stack_get());
class Test1 {
public function __destruct() {
new Test1;
}
}
function replace() {
function replace2() {
return preg_replace_callback('#.#', function () {
replace2();
}, 'x');
}
function replace() {
static $once = false;
return preg_replace_callback('#.#', function () use (&$once) {
try {
replace();
} finally {
new Test1();
if (!$once) {
$once = true;
replace2();
}
}
}, 'x');
}

View File

@ -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--
<?php

View File

@ -0,0 +1,13 @@
<?php
if (!class_exists("constructs_in_destructor")) {
class constructs_in_destructor {
public function __destruct() {
$a = new constructs_in_destructor;
$time = '';
require(__FILE__);
}
}
}
$a = new constructs_in_destructor;

View File

@ -0,0 +1,13 @@
<?php
if (!class_exists("constructs_in_destructor")) {
class constructs_in_destructor {
public function __destruct() {
$a = new constructs_in_destructor;
$time = '';
require(__FILE__);
}
}
}
$a = new constructs_in_destructor;

View File

@ -0,0 +1,23 @@
--TEST--
Stack limit 014 - Fuzzer
--SKIPIF--
<?php
if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_call_stack_get() is not available");
?>
--EXTENSIONS--
zend_test
--INI--
; The test may use a large amount of memory on systems with a large stack limit
memory_limit=1G
--FILE--
<?php
try {
require __DIR__.'/stack_limit_014.inc';
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECTF--
%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

View File

@ -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) {

View File

@ -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;

View File

@ -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)

View File

@ -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 */