Skip to content

Commit a11c8a3

Browse files
authored
Limit stack size (#9104)
1 parent dc54e04 commit a11c8a3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1844
-12
lines changed

.appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ environment:
2828
#PDO_MYSQL_TEST_PASS: Password12!
2929
#PGSQL_TEST_CONNSTR: "host=127.0.0.1 dbname=test port=5432 user=postgres password=Password12!"
3030
#PDO_PGSQL_TEST_DSN: "pgsql:host=127.0.0.1 port=5432 dbname=test user=postgres password=Password12!"
31+
STACK_LIMIT_DEFAULTS_CHECK: 1
3132
#build permutations
3233
matrix:
3334
- THREAD_SAFE: 0

.cirrus.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,5 @@ freebsd_task:
201201
tests_script:
202202
- export SKIP_IO_CAPTURE_TESTS=1
203203
- export CI_NO_IPV6=1
204+
- export STACK_LIMIT_DEFAULTS_CHECK=1
204205
- sapi/cli/php run-tests.php -P -q -j2 -g FAIL,BORK,LEAK,XLEAK --no-progress --offline --show-diff --show-slow 1000 --set-timeout 120 -d zend_extension=opcache.so

.github/actions/test-linux/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ runs:
3030
export PDO_OCI_TEST_DSN="oci:dbname=localhost/XEPDB1;charset=AL32UTF8"
3131
export SKIP_IO_CAPTURE_TESTS=1
3232
export TEST_PHP_JUNIT=junit.out.xml
33+
export STACK_LIMIT_DEFAULTS_CHECK=1
3334
sapi/cli/php run-tests.php -P -q ${{ inputs.runTestsParameters }} \
3435
-j$(/usr/bin/nproc) \
3536
-g FAIL,BORK,LEAK,XLEAK \

.github/actions/test-macos/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ runs:
1515
export SKIP_IO_CAPTURE_TESTS=1
1616
export CI_NO_IPV6=1
1717
export TEST_PHP_JUNIT=junit.out.xml
18+
export STACK_LIMIT_DEFAULTS_CHECK=1
1819
sapi/cli/php run-tests.php -P -q ${{ inputs.runTestsParameters }} \
1920
-j$(sysctl -n hw.ncpu) \
2021
-g FAIL,BORK,LEAK,XLEAK \

Zend/Zend.m4

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,37 @@ _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 gettid)
150+
151+
dnl Test whether the stack grows downwards
152+
dnl Assumes contiguous stack
153+
AC_MSG_CHECKING(whether the stack grows downwards)
154+
155+
AC_RUN_IFELSE([AC_LANG_SOURCE([[
156+
#include <stdint.h>
157+
158+
int (*volatile f)(uintptr_t);
159+
160+
int stack_grows_downwards(uintptr_t arg) {
161+
int local;
162+
return (uintptr_t)&local < arg;
163+
}
164+
165+
int main() {
166+
int local;
167+
168+
f = stack_grows_downwards;
169+
return f((uintptr_t)&local) ? 0 : 1;
170+
}
171+
]])], [
172+
AC_DEFINE([ZEND_STACK_GROWS_DOWNWARDS], 1, [Define if the stack grows downwards])
173+
AC_DEFINE([ZEND_CHECK_STACK_LIMIT], 1, [Define if checking the stack limit is supported])
174+
AC_MSG_RESULT(yes)
175+
], [
176+
AC_MSG_RESULT(no)
177+
], [
178+
AC_MSG_RESULT(no)
179+
])
150180
151181
ZEND_CHECK_FLOAT_PRECISION
152182
])
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
--TEST--
2+
Stack limit 001 - Stack limit checks with max_allowed_stack_size detection
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
; The test may use a large amount of memory on systems with a large stack limit
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+
class Test3 {
26+
public function __sleep()
27+
{
28+
serialize($this);
29+
}
30+
}
31+
32+
function replace() {
33+
return preg_replace_callback('#.#', function () {
34+
return replace();
35+
}, 'x');
36+
}
37+
38+
try {
39+
new Test1;
40+
} catch (Error $e) {
41+
echo $e->getMessage(), "\n";
42+
}
43+
44+
try {
45+
clone new Test2;
46+
} catch (Error $e) {
47+
echo $e->getMessage(), "\n";
48+
}
49+
50+
try {
51+
serialize(new Test3);
52+
} catch (Error $e) {
53+
echo $e->getMessage(), "\n";
54+
}
55+
56+
try {
57+
replace();
58+
} catch (Error $e) {
59+
echo $e->getMessage(), "\n";
60+
}
61+
62+
?>
63+
--EXPECTF--
64+
array(4) {
65+
["base"]=>
66+
string(%d) "0x%x"
67+
["max_size"]=>
68+
string(%d) "0x%x"
69+
["position"]=>
70+
string(%d) "0x%x"
71+
["EG(stack_limit)"]=>
72+
string(%d) "0x%x"
73+
}
74+
Maximum call stack size of %d bytes reached. Infinite recursion?
75+
Maximum call stack size of %d bytes reached. Infinite recursion?
76+
Maximum call stack size of %d bytes reached. Infinite recursion?
77+
Maximum call stack size of %d bytes reached. Infinite recursion?
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
--TEST--
2+
Stack limit 002 - Stack limit checks with max_allowed_stack_size detection (fibers)
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
fiber.stack_size=512k
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+
class Test3 {
25+
public function __sleep()
26+
{
27+
serialize($this);
28+
}
29+
}
30+
31+
function replace() {
32+
return preg_replace_callback('#.#', function () {
33+
return replace();
34+
}, 'x');
35+
}
36+
37+
$fiber = new Fiber(function (): void {
38+
try {
39+
new Test1;
40+
} catch (Error $e) {
41+
echo $e->getMessage(), "\n";
42+
}
43+
44+
try {
45+
clone new Test2;
46+
} catch (Error $e) {
47+
echo $e->getMessage(), "\n";
48+
}
49+
50+
try {
51+
serialize(new Test3);
52+
} catch (Error $e) {
53+
echo $e->getMessage(), "\n";
54+
}
55+
56+
try {
57+
replace();
58+
} catch (Error $e) {
59+
echo $e->getMessage(), "\n";
60+
}
61+
});
62+
63+
$fiber->start();
64+
65+
?>
66+
--EXPECTF--
67+
array(4) {
68+
["base"]=>
69+
string(%d) "0x%x"
70+
["max_size"]=>
71+
string(%d) "0x%x"
72+
["position"]=>
73+
string(%d) "0x%x"
74+
["EG(stack_limit)"]=>
75+
string(%d) "0x%x"
76+
}
77+
Maximum call stack size of %d bytes reached. Infinite recursion?
78+
Maximum call stack size of %d bytes reached. Infinite recursion?
79+
Maximum call stack size of %d bytes reached. Infinite recursion?
80+
Maximum call stack size of %d bytes reached. Infinite recursion?
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--TEST--
2+
Stack limit 003 - 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 of %d bytes reached. Infinite recursion?
61+
Maximum call stack size of %d bytes reached. Infinite recursion?
62+
Maximum call stack size of %d bytes reached. Infinite recursion?
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', '400K');
27+
$fiber = new Fiber($callback);
28+
$fiber->start();
29+
$depth1 = $fiber->getReturn();
30+
31+
ini_set('fiber.stack_size', '200K');
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)

0 commit comments

Comments
 (0)