Skip to content

Commit

Permalink
Limit stack usage
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaud-lb committed Sep 16, 2022
1 parent 17b0e49 commit b669a8a
Show file tree
Hide file tree
Showing 32 changed files with 1,501 additions and 13 deletions.
51 changes: 50 additions & 1 deletion Zend/Zend.m4
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,56 @@ _LT_AC_TRY_DLOPEN_SELF([
])
dnl Checks for library functions.
AC_CHECK_FUNCS(getpid kill sigsetjmp)
AC_CHECK_FUNCS(getpid kill sigsetjmp pthread_getattr_np pthread_attr_get_np pthread_get_stackaddr_np pthread_attr_getstack pthread_attr_getstacksize pthread_attr_getstackaddr)
dnl Test whether the target system has __libc_stack_end
AC_MSG_CHECKING(whether libc has __libc_stack_end)
AC_RUN_IFELSE([AC_LANG_SOURCE([[
extern void* __libc_stack_end;
int main()
{
void *test = __libc_stack_end;
return 0;
}
]])], [
AC_DEFINE([HAVE_LIBC_STACK_END], 1, [Define if the target system has __libc_stack_end])
AC_MSG_RESULT(yes)
], [
AC_MSG_RESULT(no)
], [
AC_MSG_RESULT(no)
])
dnl Test whether the stack grows downwards
dnl Assumes contiguous stack
AC_MSG_CHECKING(whether the stack grows downwards)
AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <stdint.h>
int (*volatile f)(uintptr_t);
int stack_grows_downwards(uintptr_t arg) {
int local;
return (uintptr_t)&local < arg;
}
int main() {
int local;
f = stack_grows_downwards;
return f((uintptr_t)&local) ? 0 : 1;
}
]])], [
AC_DEFINE([ZEND_STACK_GROWS_DOWNWARDS], 1, [Define if the stack grows downwards])
AC_DEFINE([ZEND_CHECK_STACK_LIMIT], 1, [Define if checking the stack limit is supported])
AC_MSG_RESULT(yes)
], [
AC_MSG_RESULT(no)
], [
AC_MSG_RESULT(no)
])
ZEND_CHECK_FLOAT_PRECISION
])
Expand Down
63 changes: 63 additions & 0 deletions Zend/tests/stack_limit_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
--TEST--
Stack limit 001 - Stack limit checks with max_allowed_stack_size detection
--EXTENSIONS--
zend_test
--INI--
; TODO
memory_limit=2G
--FILE--
<?php

var_dump(zend_test_zend_call_stack_get());

class Test1 {
public function __destruct() {
new Test1;
}
}

class Test2 {
public function __clone() {
clone $this;
}
}

function replace() {
return preg_replace_callback('#.#', function () {
return replace();
}, 'x');
}

try {
new Test1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

try {
clone new Test2;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

try {
replace();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECTF--
array(4) {
["base"]=>
string(%d) "0x%x"
["max_size"]=>
string(%d) "0x%x"
["position"]=>
string(%d) "0x%x"
["EG(stack_limit)"]=>
string(%d) "0x%x"
}
Maximum call stack size reached. Infinite recursion?
Maximum call stack size reached. Infinite recursion?
Maximum call stack size reached. Infinite recursion?
64 changes: 64 additions & 0 deletions Zend/tests/stack_limit_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
--TEST--
Stack limit 002 - Stack limit checks with max_allowed_stack_size detection (fibers)
--EXTENSIONS--
zend_test
--FILE--
<?php

var_dump(zend_test_zend_call_stack_get());

class Test1 {
public function __destruct() {
new Test1;
}
}

class Test2 {
public function __clone() {
clone $this;
}
}

function replace() {
return preg_replace_callback('#.#', function () {
return replace();
}, 'x');
}

$fiber = new Fiber(function (): void {
try {
new Test1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

try {
clone new Test2;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

try {
replace();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
});

$fiber->start();

?>
--EXPECTF--
array(4) {
["base"]=>
string(%d) "0x%x"
["max_size"]=>
string(%d) "0x%x"
["position"]=>
string(%d) "0x%x"
["EG(stack_limit)"]=>
string(%d) "0x%x"
}
Maximum call stack size reached. Infinite recursion?
Maximum call stack size reached. Infinite recursion?
Maximum call stack size reached. Infinite recursion?
62 changes: 62 additions & 0 deletions Zend/tests/stack_limit_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
--TEST--
Stack limit 002 - Stack limit checks with fixed max_allowed_stack_size
--EXTENSIONS--
zend_test
--INI--
zend.max_allowed_stack_size=128K
--FILE--
<?php

var_dump(zend_test_zend_call_stack_get());

class Test1 {
public function __destruct() {
new Test1;
}
}

class Test2 {
public function __clone() {
clone $this;
}
}

function replace() {
return preg_replace_callback('#.#', function () {
return replace();
}, 'x');
}

try {
new Test1;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

try {
clone new Test2;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

try {
replace();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECTF--
array(4) {
["base"]=>
string(%d) "0x%x"
["max_size"]=>
string(%d) "0x%x"
["position"]=>
string(%d) "0x%x"
["EG(stack_limit)"]=>
string(%d) "0x%x"
}
Maximum call stack size reached. Infinite recursion?
Maximum call stack size reached. Infinite recursion?
Maximum call stack size reached. Infinite recursion?
50 changes: 50 additions & 0 deletions Zend/tests/stack_limit_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
Stack limit 004 - Stack limit checks with fixed max_allowed_stack_size (fibers)
--EXTENSIONS--
zend_test
--FILE--
<?php

var_dump(zend_test_zend_call_stack_get());

class Test1 {
public function __destruct() {
new Test1;
}
}

$callback = function (): int {
try {
new Test1;
} catch (Error $e) {
return count($e->getTrace());
}

throw new \Exception();
};

ini_set('fiber.stack_size', '100K');
$fiber = new Fiber($callback);
$fiber->start();
$depth1 = $fiber->getReturn();

ini_set('fiber.stack_size', '50K');
$fiber = new Fiber($callback);
$fiber->start();
$depth2 = $fiber->getReturn();

var_dump($depth1 > $depth2);

?>
--EXPECTF--
array(4) {
["base"]=>
string(%d) "0x%x"
["max_size"]=>
string(%d) "0x%x"
["position"]=>
string(%d) "0x%x"
["EG(stack_limit)"]=>
string(%d) "0x%x"
}
bool(true)
64 changes: 64 additions & 0 deletions Zend/tests/stack_limit_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
--TEST--
Stack limit 005 - Internal stack limit check in zend_compile_expr()
--EXTENSIONS--
zend_test
--INI--
zend.max_allowed_stack_size=128K
--FILE--
<?php

$test
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
;

--EXPECTF--
Fatal error: Maximum call stack size reached. Try splitting expression in %s on line 3
Loading

0 comments on commit b669a8a

Please sign in to comment.