Skip to content

Commit b669a8a

Browse files
committed
Limit stack usage
1 parent 17b0e49 commit b669a8a

32 files changed

+1501
-13
lines changed

Zend/Zend.m4

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,56 @@ _LT_AC_TRY_DLOPEN_SELF([
146146
])
147147
148148
dnl Checks for library functions.
149-
AC_CHECK_FUNCS(getpid kill sigsetjmp)
149+
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)
150+
151+
dnl Test whether the target system has __libc_stack_end
152+
AC_MSG_CHECKING(whether libc has __libc_stack_end)
153+
154+
AC_RUN_IFELSE([AC_LANG_SOURCE([[
155+
extern void* __libc_stack_end;
156+
int main()
157+
{
158+
void *test = __libc_stack_end;
159+
return 0;
160+
}
161+
]])], [
162+
AC_DEFINE([HAVE_LIBC_STACK_END], 1, [Define if the target system has __libc_stack_end])
163+
AC_MSG_RESULT(yes)
164+
], [
165+
AC_MSG_RESULT(no)
166+
], [
167+
AC_MSG_RESULT(no)
168+
])
169+
170+
dnl Test whether the stack grows downwards
171+
dnl Assumes contiguous stack
172+
AC_MSG_CHECKING(whether the stack grows downwards)
173+
174+
AC_RUN_IFELSE([AC_LANG_SOURCE([[
175+
#include <stdint.h>
176+
177+
int (*volatile f)(uintptr_t);
178+
179+
int stack_grows_downwards(uintptr_t arg) {
180+
int local;
181+
return (uintptr_t)&local < arg;
182+
}
183+
184+
int main() {
185+
int local;
186+
187+
f = stack_grows_downwards;
188+
return f((uintptr_t)&local) ? 0 : 1;
189+
}
190+
]])], [
191+
AC_DEFINE([ZEND_STACK_GROWS_DOWNWARDS], 1, [Define if the stack grows downwards])
192+
AC_DEFINE([ZEND_CHECK_STACK_LIMIT], 1, [Define if checking the stack limit is supported])
193+
AC_MSG_RESULT(yes)
194+
], [
195+
AC_MSG_RESULT(no)
196+
], [
197+
AC_MSG_RESULT(no)
198+
])
150199
151200
ZEND_CHECK_FLOAT_PRECISION
152201
])

Zend/tests/stack_limit_001.phpt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
--TEST--
2+
Stack limit 001 - Stack limit checks with max_allowed_stack_size detection
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
; TODO
7+
memory_limit=2G
8+
--FILE--
9+
<?php
10+
11+
var_dump(zend_test_zend_call_stack_get());
12+
13+
class Test1 {
14+
public function __destruct() {
15+
new Test1;
16+
}
17+
}
18+
19+
class Test2 {
20+
public function __clone() {
21+
clone $this;
22+
}
23+
}
24+
25+
function replace() {
26+
return preg_replace_callback('#.#', function () {
27+
return replace();
28+
}, 'x');
29+
}
30+
31+
try {
32+
new Test1;
33+
} catch (Error $e) {
34+
echo $e->getMessage(), "\n";
35+
}
36+
37+
try {
38+
clone new Test2;
39+
} catch (Error $e) {
40+
echo $e->getMessage(), "\n";
41+
}
42+
43+
try {
44+
replace();
45+
} catch (Error $e) {
46+
echo $e->getMessage(), "\n";
47+
}
48+
49+
?>
50+
--EXPECTF--
51+
array(4) {
52+
["base"]=>
53+
string(%d) "0x%x"
54+
["max_size"]=>
55+
string(%d) "0x%x"
56+
["position"]=>
57+
string(%d) "0x%x"
58+
["EG(stack_limit)"]=>
59+
string(%d) "0x%x"
60+
}
61+
Maximum call stack size reached. Infinite recursion?
62+
Maximum call stack size reached. Infinite recursion?
63+
Maximum call stack size reached. Infinite recursion?

Zend/tests/stack_limit_002.phpt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
Stack limit 002 - Stack limit checks with max_allowed_stack_size detection (fibers)
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
var_dump(zend_test_zend_call_stack_get());
9+
10+
class Test1 {
11+
public function __destruct() {
12+
new Test1;
13+
}
14+
}
15+
16+
class Test2 {
17+
public function __clone() {
18+
clone $this;
19+
}
20+
}
21+
22+
function replace() {
23+
return preg_replace_callback('#.#', function () {
24+
return replace();
25+
}, 'x');
26+
}
27+
28+
$fiber = new Fiber(function (): void {
29+
try {
30+
new Test1;
31+
} catch (Error $e) {
32+
echo $e->getMessage(), "\n";
33+
}
34+
35+
try {
36+
clone new Test2;
37+
} catch (Error $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
41+
try {
42+
replace();
43+
} catch (Error $e) {
44+
echo $e->getMessage(), "\n";
45+
}
46+
});
47+
48+
$fiber->start();
49+
50+
?>
51+
--EXPECTF--
52+
array(4) {
53+
["base"]=>
54+
string(%d) "0x%x"
55+
["max_size"]=>
56+
string(%d) "0x%x"
57+
["position"]=>
58+
string(%d) "0x%x"
59+
["EG(stack_limit)"]=>
60+
string(%d) "0x%x"
61+
}
62+
Maximum call stack size reached. Infinite recursion?
63+
Maximum call stack size reached. Infinite recursion?
64+
Maximum call stack size reached. Infinite recursion?

Zend/tests/stack_limit_003.phpt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--TEST--
2+
Stack limit 002 - Stack limit checks with fixed max_allowed_stack_size
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend.max_allowed_stack_size=128K
7+
--FILE--
8+
<?php
9+
10+
var_dump(zend_test_zend_call_stack_get());
11+
12+
class Test1 {
13+
public function __destruct() {
14+
new Test1;
15+
}
16+
}
17+
18+
class Test2 {
19+
public function __clone() {
20+
clone $this;
21+
}
22+
}
23+
24+
function replace() {
25+
return preg_replace_callback('#.#', function () {
26+
return replace();
27+
}, 'x');
28+
}
29+
30+
try {
31+
new Test1;
32+
} catch (Error $e) {
33+
echo $e->getMessage(), "\n";
34+
}
35+
36+
try {
37+
clone new Test2;
38+
} catch (Error $e) {
39+
echo $e->getMessage(), "\n";
40+
}
41+
42+
try {
43+
replace();
44+
} catch (Error $e) {
45+
echo $e->getMessage(), "\n";
46+
}
47+
48+
?>
49+
--EXPECTF--
50+
array(4) {
51+
["base"]=>
52+
string(%d) "0x%x"
53+
["max_size"]=>
54+
string(%d) "0x%x"
55+
["position"]=>
56+
string(%d) "0x%x"
57+
["EG(stack_limit)"]=>
58+
string(%d) "0x%x"
59+
}
60+
Maximum call stack size reached. Infinite recursion?
61+
Maximum call stack size reached. Infinite recursion?
62+
Maximum call stack size reached. Infinite recursion?

Zend/tests/stack_limit_004.phpt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
Stack limit 004 - Stack limit checks with fixed max_allowed_stack_size (fibers)
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
var_dump(zend_test_zend_call_stack_get());
9+
10+
class Test1 {
11+
public function __destruct() {
12+
new Test1;
13+
}
14+
}
15+
16+
$callback = function (): int {
17+
try {
18+
new Test1;
19+
} catch (Error $e) {
20+
return count($e->getTrace());
21+
}
22+
23+
throw new \Exception();
24+
};
25+
26+
ini_set('fiber.stack_size', '100K');
27+
$fiber = new Fiber($callback);
28+
$fiber->start();
29+
$depth1 = $fiber->getReturn();
30+
31+
ini_set('fiber.stack_size', '50K');
32+
$fiber = new Fiber($callback);
33+
$fiber->start();
34+
$depth2 = $fiber->getReturn();
35+
36+
var_dump($depth1 > $depth2);
37+
38+
?>
39+
--EXPECTF--
40+
array(4) {
41+
["base"]=>
42+
string(%d) "0x%x"
43+
["max_size"]=>
44+
string(%d) "0x%x"
45+
["position"]=>
46+
string(%d) "0x%x"
47+
["EG(stack_limit)"]=>
48+
string(%d) "0x%x"
49+
}
50+
bool(true)

Zend/tests/stack_limit_005.phpt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
Stack limit 005 - Internal stack limit check in zend_compile_expr()
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend.max_allowed_stack_size=128K
7+
--FILE--
8+
<?php
9+
10+
$test
11+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
12+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
13+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
14+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
15+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
16+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
17+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
18+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
19+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
20+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
21+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
22+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
23+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
24+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
25+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
26+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
27+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
28+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
29+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
30+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
31+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
32+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
33+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
34+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
35+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
36+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
37+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
38+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
39+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
40+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
41+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
42+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
43+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
44+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
45+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
46+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
47+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
48+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
49+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
50+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
51+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
52+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
53+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
54+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
55+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
56+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
57+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
58+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
59+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
60+
->f()->f()->f()->f()->f()->f()->f()->f()->f()->f()
61+
;
62+
63+
--EXPECTF--
64+
Fatal error: Maximum call stack size reached. Try splitting expression in %s on line 3

0 commit comments

Comments
 (0)