diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 index fd00dc7270de5..6a86284748fb1 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -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 + +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 ]) diff --git a/Zend/tests/stack_limit_001.phpt b/Zend/tests/stack_limit_001.phpt new file mode 100644 index 0000000000000..d866fd1aa179f --- /dev/null +++ b/Zend/tests/stack_limit_001.phpt @@ -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-- +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? diff --git a/Zend/tests/stack_limit_002.phpt b/Zend/tests/stack_limit_002.phpt new file mode 100644 index 0000000000000..2e887fa94a11a --- /dev/null +++ b/Zend/tests/stack_limit_002.phpt @@ -0,0 +1,64 @@ +--TEST-- +Stack limit 002 - Stack limit checks with max_allowed_stack_size detection (fibers) +--EXTENSIONS-- +zend_test +--FILE-- +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? diff --git a/Zend/tests/stack_limit_003.phpt b/Zend/tests/stack_limit_003.phpt new file mode 100644 index 0000000000000..a2d785fd707f5 --- /dev/null +++ b/Zend/tests/stack_limit_003.phpt @@ -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-- +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? diff --git a/Zend/tests/stack_limit_004.phpt b/Zend/tests/stack_limit_004.phpt new file mode 100644 index 0000000000000..76876c0ca3018 --- /dev/null +++ b/Zend/tests/stack_limit_004.phpt @@ -0,0 +1,50 @@ +--TEST-- +Stack limit 004 - Stack limit checks with fixed max_allowed_stack_size (fibers) +--EXTENSIONS-- +zend_test +--FILE-- +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) diff --git a/Zend/tests/stack_limit_005.phpt b/Zend/tests/stack_limit_005.phpt new file mode 100644 index 0000000000000..a272250b5f8c1 --- /dev/null +++ b/Zend/tests/stack_limit_005.phpt @@ -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-- +f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->f()->f()->f()->f() + ->f()->f()->f()->f()->f()->f()->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 diff --git a/Zend/tests/stack_limit_006.phpt b/Zend/tests/stack_limit_006.phpt new file mode 100644 index 0000000000000..1a358b936e127 --- /dev/null +++ b/Zend/tests/stack_limit_006.phpt @@ -0,0 +1,320 @@ +--TEST-- +Stack limit 006 - env size affects __libc_stack_end +--EXTENSIONS-- +zend_test +--INI-- +; TODO +memory_limit=2G +--ENV-- +ENV001=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV002=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV003=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV004=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV005=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV006=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV007=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV008=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV009=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV010=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV011=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV012=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV013=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV014=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV015=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV016=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV017=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV018=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV019=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV020=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV021=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV022=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV023=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV024=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV025=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV026=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV027=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV028=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV029=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV030=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV031=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV032=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV033=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV034=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV035=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV036=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV037=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV038=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV039=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV040=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV041=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV042=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV043=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV044=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV045=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV046=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV047=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV048=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV049=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV050=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV051=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV052=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV053=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV054=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV055=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV056=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV057=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV058=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV059=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV060=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV061=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV062=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV063=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV064=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV065=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV066=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV067=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV068=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV069=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV070=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV071=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV072=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV073=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV074=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV075=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV076=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV077=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV078=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV079=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV080=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV081=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV082=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV083=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV084=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV085=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV086=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV087=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV088=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV089=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV090=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV091=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV092=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV093=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV094=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV095=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV096=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV097=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV098=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV099=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV100=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV101=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV102=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV103=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV104=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV105=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV106=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV107=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV108=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV109=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV110=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV111=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV112=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV113=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV114=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV115=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV116=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV117=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV118=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV119=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV120=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV121=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV122=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV123=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV124=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV125=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV126=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV127=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV128=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV129=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV130=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV131=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV132=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV133=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV134=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV135=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV136=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV137=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV138=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV139=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV140=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV141=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV142=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV143=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV144=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV145=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV146=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV147=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV148=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV149=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV150=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV151=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV152=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV153=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV154=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV155=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV156=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV157=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV158=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV159=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV160=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV161=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV162=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV163=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV164=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV165=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV166=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV167=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV168=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV169=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV170=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV171=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV172=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV173=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV174=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV175=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV176=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV177=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV178=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV179=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV180=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV181=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV182=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV183=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV184=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV185=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV186=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV187=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV188=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV189=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV190=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV191=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV192=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV193=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV194=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV195=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV196=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV197=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV198=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV199=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV200=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV201=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV202=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV203=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV204=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV205=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV206=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV207=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV208=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV209=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV210=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV211=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV212=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV213=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV214=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV215=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV216=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV217=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV218=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV219=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV220=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV221=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV222=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV223=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV224=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV225=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV226=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV227=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV228=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV229=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV230=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV231=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV232=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV233=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV234=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV235=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV236=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV237=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV238=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV239=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV240=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV241=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV242=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV243=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV244=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV245=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV246=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV247=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV248=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV249=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV250=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV251=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV252=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV253=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV254=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV255=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +ENV256=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +--FILE-- +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? diff --git a/Zend/tests/stack_limit_007.phpt b/Zend/tests/stack_limit_007.phpt new file mode 100644 index 0000000000000..d48c1f6ce94d1 --- /dev/null +++ b/Zend/tests/stack_limit_007.phpt @@ -0,0 +1,41 @@ +--TEST-- +Stack limit 007 - Exception handling +--EXTENSIONS-- +zend_test +--INI-- +zend.max_allowed_stack_size=128K +--FILE-- +getMessage(), "\n"; + // We should not enter the catch block if we haven't executed at + // least one op in the try block + printf("Try executed: %d\n", $tryExecuted ?? 0); + } + }, 'x'); +} + +replace(); + +?> +--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? +Try executed: 1 diff --git a/Zend/tests/stack_limit_008.phpt b/Zend/tests/stack_limit_008.phpt new file mode 100644 index 0000000000000..f0e6f12f07350 --- /dev/null +++ b/Zend/tests/stack_limit_008.phpt @@ -0,0 +1,54 @@ +--TEST-- +Stack limit 008 - Exception handling +--EXTENSIONS-- +zend_test +--INI-- +zend.max_allowed_stack_size=128K +--FILE-- +getMessage(); + } + } + }, 'x'); +} + +function replace2() { + return preg_replace_callback('#.#', function () { + try { + return ''; + } finally { + // We should not enter the finally block if we haven't executed at + // least one op in the try block + echo "Finally block: This should not execute\n"; + } + }, 'x'); +} + +replace(); + +?> +--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" +} +Will throw: +Maximum call stack size reached. Infinite recursion? diff --git a/Zend/tests/stack_limit_009.phpt b/Zend/tests/stack_limit_009.phpt new file mode 100644 index 0000000000000..08f14495e4370 --- /dev/null +++ b/Zend/tests/stack_limit_009.phpt @@ -0,0 +1,24 @@ +--TEST-- +Stack limit 009 - Check that we can actually use all the stack +--EXTENSIONS-- +zend_test +--FILE-- + +--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" +} +int(%d) diff --git a/Zend/zend.c b/Zend/zend.c index c6a5f7f91ce0f..b2900ae4816e4 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -35,6 +35,7 @@ #include "zend_attributes.h" #include "zend_observer.h" #include "zend_fibers.h" +#include "zend_call_stack.h" #include "Optimizer/zend_optimizer.h" static size_t global_map_ptr_last = 0; @@ -173,6 +174,53 @@ static ZEND_INI_MH(OnSetExceptionStringParamMaxLen) /* {{{ */ } /* }}} */ +#ifdef ZEND_CHECK_STACK_LIMIT +static ZEND_INI_MH(OnUpdateMaxAllowedStackSize) /* {{{ */ +{ + zend_long value; + + value = zend_ini_parse_quantity_warn(new_value, entry->name); + + if (value < ZEND_MAX_ALLOWED_STACK_USAGE_UNCHECKED) { + zend_error(E_WARNING, "Invalid \"%s\" setting. Value must be >= %d, but got " ZEND_LONG_FMT, + ZSTR_VAL(entry->name), ZEND_MAX_ALLOWED_STACK_USAGE_UNCHECKED, value); + return FAILURE; + } + + EG(max_allowed_stack_size) = value; + return SUCCESS; +} +/* }}} */ + +static ZEND_INI_MH(OnUpdateReservedStackSize) /* {{{ */ +{ + zend_ulong size = zend_ini_parse_uquantity_warn(new_value, entry->name); + + /* Min value accounts for alloca, PCRE2 START_FRAMES_SIZE, and some buffer + * for normal function calls. + * We could reduce this on systems without alloca if we also add stack size + * checks before pcre2_match(). */ +#ifdef ZEND_ALLOCA_MAX_SIZE + zend_ulong min = ZEND_ALLOCA_MAX_SIZE + 16*1024; +#else + zend_ulong min = 32*1024; +#endif + + if (size == 0) { + size = min; + } else if (size < min) { + zend_error(E_WARNING, "%s should not be lower than " ZEND_ULONG_FMT ", but got " ZEND_ULONG_FMT "\n", + ZSTR_VAL(entry->name), min, size); + return FAILURE; + } + + EG(reserved_stack_size) = size; + + return SUCCESS; +} +/* }}} */ +#endif /* ZEND_CHECK_STACK_LIMIT */ + static ZEND_INI_MH(OnUpdateFiberStackSize) /* {{{ */ { if (new_value) { @@ -203,6 +251,12 @@ ZEND_INI_BEGIN() STD_ZEND_INI_BOOLEAN("zend.exception_ignore_args", "0", ZEND_INI_ALL, OnUpdateBool, exception_ignore_args, zend_executor_globals, executor_globals) STD_ZEND_INI_ENTRY("zend.exception_string_param_max_len", "15", ZEND_INI_ALL, OnSetExceptionStringParamMaxLen, exception_string_param_max_len, zend_executor_globals, executor_globals) STD_ZEND_INI_ENTRY("fiber.stack_size", NULL, ZEND_INI_ALL, OnUpdateFiberStackSize, fiber_stack_size, zend_executor_globals, executor_globals) +#ifdef ZEND_CHECK_STACK_LIMIT + /* The maximum allowed call stack size. 0: auto detect, -1: no limit. For fibers, this is fiber.stack_size. */ + STD_ZEND_INI_ENTRY("zend.max_allowed_stack_size", "0", ZEND_INI_PERDIR, OnUpdateMaxAllowedStackSize, max_allowed_stack_size, zend_executor_globals, executor_globals) + /* Substracted from the max allowed stack size, as a buffer, when checking for overflow. 0: auto detect. */ + STD_ZEND_INI_ENTRY("zend.reserved_stack_size", "0", ZEND_INI_PERDIR, OnUpdateReservedStackSize, reserved_stack_size, zend_executor_globals, executor_globals) +#endif ZEND_INI_END() @@ -795,6 +849,12 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ executor_globals->record_errors = false; executor_globals->num_errors = 0; executor_globals->errors = NULL; + +# ifdef ZEND_CHECK_STACK_LIMIT + if (!zend_call_stack_get(&executor_globals->call_stack)) { + executor_globals->call_stack = (zend_call_stack){0}; + } +# endif /* ZEND_CHECK_STACK_LIMIT */ } /* }}} */ @@ -981,6 +1041,12 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ CG(map_ptr_base) = ZEND_MAP_PTR_BIASED_BASE(NULL); CG(map_ptr_size) = 0; CG(map_ptr_last) = 0; + +# ifdef ZEND_CHECK_STACK_LIMIT + if (!zend_call_stack_get(&EG(call_stack))) { + EG(call_stack) = (zend_call_stack){0}; + } +# endif /* ZEND_CHECK_STACK_LIMIT */ #endif EG(error_reporting) = E_ALL & ~E_NOTICE; diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c new file mode 100644 index 0000000000000..45f0e02ed53f6 --- /dev/null +++ b/Zend/zend_call_stack.c @@ -0,0 +1,335 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ + +/* Inspired from Chromium's stack_util.cc */ + +#include "zend.h" +#include "zend_portability.h" +#include "zend_call_stack.h" +#include +#ifdef ZEND_WIN32 +# include +# include +#else /* ZEND_WIN32 */ +# include +# ifdef HAVE_UNISTD_H +# include +# endif +# ifdef HAVE_SYS_TYPES_H +# include +# endif +#endif /* ZEND_WIN32 */ +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) +# include +#endif +#ifdef __FreeBSD__ +# include +#endif + +#ifdef __linux__ +static bool zend_call_stack_is_main_thread(void) { + return getpid() == gettid(); +} + +# ifdef HAVE_PTHREAD_GETATTR_NP +static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) +{ + pthread_attr_t attr; + int error; + void *addr; + size_t max_size; + + /* pthread_getattr_np() will return bogus values for the main thread with + * musl or with some old glibc versions */ + ZEND_ASSERT(!zend_call_stack_is_main_thread()); + + error = pthread_getattr_np(pthread_self(), &attr); + if (error) { + return false; + } + + error = pthread_attr_getstack(&attr, &addr, &max_size); + if (error) { + return false; + } + +# if defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8)) + { + size_t guard_size; + /* In glibc prior to 2.8, addr and size include the guard pages */ + error = pthread_attr_getguardsize(&attr, &guard_size); + if (error) { + return false; + } + + addr = (int8_t*)addr + guard_size; + max_size -= guard_size; + } +# endif /* glibc < 2.8 */ + + stack->base = (int8_t*)addr + max_size; + stack->max_size = max_size; + + return true; +} +# else /* HAVE_PTHREAD_GETATTR_NP */ +static bool zend_call_stack_get_linux_pthread(zend_call_stack *stack) +{ + return false; +} +# endif /* HAVE_PTHREAD_GETATTR_NP */ + +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 start, end, prev_end = 0; + size_t max_size; + bool found = false; + struct rlimit rlim; + int error; + + /* This method is relevant only for the main thread */ + ZEND_ASSERT(zend_call_stack_is_main_thread()); + + /* Scan the process memory mappings to find the one containing the stack. + * + * The end of the stack mapping is the base of the stack. The start is + * adjusted by the kernel as the stack grows. The maximum stack size is + * determined by RLIMIT_STACK and the previous mapping. + * + * + * ^ Higher addresses ^ + * : : + * : : + * Mapping end --> |-------------------| <-- Stack base (stack start) + * | | ^ + * | Stack Mapping | | Stack size + * | | v + * Mapping start --> |-------------------| <-- Current stack end + * (adjusted : : + * downards as the . . + * stack grows) : : + * |-------------------| + * | Some Mapping | The previous mapping may prevent + * |-------------------| stack growth + * : : + * : : + * v Lower addresses v + */ + + f = fopen("/proc/self/maps", "r"); + if (!f) { + return false; + } + + while (fgets(buffer, sizeof(buffer), f) && sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR, &start, &end) == 2) { + if (start <= addr_on_stack && end >= addr_on_stack) { + found = true; + break; + } + prev_end = end; + } + + fclose(f); + + if (!found) { + return false; + } + + error = getrlimit(RLIMIT_STACK, &rlim); + if (error || rlim.rlim_cur == RLIM_INFINITY) { + return false; + } + + max_size = rlim.rlim_cur; + + /* Previous mapping may prevent the stack from growing */ + if (end - max_size < prev_end) { + max_size = prev_end - end; + } + + stack->base = (void*)end; + stack->max_size = max_size; + + return true; +} + +static bool zend_call_stack_get_linux(zend_call_stack *stack) +{ + if (zend_call_stack_is_main_thread()) { + return zend_call_stack_get_linux_proc_maps(stack); + } + + return zend_call_stack_get_linux_pthread(stack); +} +#else /* __linux__ */ +static bool zend_call_stack_get_linux(zend_call_stack *stack) +{ + return false; +} +#endif /* __linux__ */ + +/* pthread_attr_getstack is only available with -pthread. We need a non-pthread + * implementation of this. + */ +#if defined(__FreeBSD__) && defined(HAVE_PTHREAD_ATTR_GET_NP) && defined(HAVE_PTHREAD_ATTR_GET_STACK) +static bool zend_call_stack_get_freebsd(zend_call_stack *stack) +{ + pthread_attr_t attr; + int error; + void *addr; + size_t max_size; + size_t guard_size; + + pthread_attr_init(&attr); + + error = pthread_attr_get_np(pthread_self(), &attr); + if (error) { + goto fail; + } + + error = pthread_attr_getstack(&attr, &addr, &max_size); + if (error) { + goto fail; + } + + /* addr and size includes the guard pages, at least for the main thread */ + error = pthread_attr_getguardsize(&attr, &guard_size); + if (error) { + return false; + } + + addr = (int8_t*)addr + guard_size; + max_size -= guard_size; + + stack->base = (int8_t*)addr + max_size; + stack->max_size = max_size; + + pthread_attr_destroy(&attr); + return true; + +fail: + pthread_attr_destroy(&attr); + return false; +} +#else /* defined(__FreeBSD__) && ... */ +static bool zend_call_stack_get_freebsd(zend_call_stack *stack) +{ + return false; +} +#endif /* defined(__FreeBSD__) && ... */ + + +#ifdef ZEND_WIN32 +static bool zend_call_stack_get_win32(zend_call_stack *stack) +{ + MEMORY_BASIC_INFORMATION stack_info; + int8_t *base; + +#ifdef _M_ARM64 + return false; +#endif + +#ifdef _M_X64 + base = (void*)((NT_TIB64*)NtCurrentTeb())->StackBase; +#else + base = (void*)((NT_TIB*)NtCurrentTeb())->StackBase; +#endif + + memset(&stack_info, 0, sizeof(MEMORY_BASIC_INFORMATION)); + size_t result_size = VirtualQuery(&stack_info, &stack_info, sizeof(MEMORY_BASIC_INFORMATION)); + ZEND_ASSERT(result_size >= sizeof(MEMORY_BASIC_INFORMATION)); + + int8_t* end = (int8_t*)stack_info.AllocationBase; + ZEND_ASSERT(base > end); + + size_t max_size = (size_t)(base - end); + + // Last pages are not usable + // http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx + ZEND_ASSERT(max_size > 4*4096); + max_size -= 4*4096; + + stack->base = base; + stack->max_size = max_size; + + return true; +} +#else /* ZEND_WIN32 */ +static bool zend_call_stack_get_win32(zend_call_stack *stack) +{ + return false; +} +#endif /* ZEND_WIN32 */ + +#if defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) +static bool zend_call_stack_get_macos(zend_call_stack *stack) +{ + void *base = pthread_get_stackaddr_np(pthread_self()); + size_t max_size; + + if (pthread_main_np()) { + /* pthread_get_stacksize_np() returns a too low value for the main + * thread in OSX 10.9, 10.10: + * https://mail.openjdk.org/pipermail/hotspot-dev/2013-October/011353.html + * https://github.com/rust-lang/rust/issues/43347 + */ + + /* Stack size is 8MiB by default for main threads + * https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html */ + max_size = 8 * 1024 * 1024; + } else { + max_size = pthread_get_stacksize_np(pthread_self()); + } + + stack->base = base; + stack->max_size = max_size; + + return true; +} +#else /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ +static bool zend_call_stack_get_macos(zend_call_stack *stack) +{ + return false; +} +#endif /* defined(__APPLE__) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) */ + +/** Get the stack information for the calling thread */ +ZEND_API bool zend_call_stack_get(zend_call_stack *stack) +{ + if (zend_call_stack_get_linux(stack)) { + return true; + } + + if (zend_call_stack_get_freebsd(stack)) { + return true; + } + + if (zend_call_stack_get_win32(stack)) { + return true; + } + + if (zend_call_stack_get_macos(stack)) { + return true; + } + + return false; +} + diff --git a/Zend/zend_call_stack.h b/Zend/zend_call_stack.h new file mode 100644 index 0000000000000..531aa5fe51c6f --- /dev/null +++ b/Zend/zend_call_stack.h @@ -0,0 +1,97 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_CALL_STACK_H +#define ZEND_CALL_STACK_H + +#include "zend.h" +#include "zend_portability.h" +#ifdef __APPLE__ +# include +#endif + +typedef struct _zend_call_stack { + void *base; + size_t max_size; +} zend_call_stack; + +ZEND_API bool zend_call_stack_get(zend_call_stack *stack); + +/** Returns an approximation of the current stack position */ +static zend_always_inline void *zend_call_stack_position(void) { +#ifdef ZEND_WIN32 + return _AddressOfReturnAddress(); +#elif HAVE_BUILTIN_FRAME_ADDRESS + return __builtin_frame_address(0); +#else + void *a; + void *pos = (void*)&a; + return pos; +#endif +} + +static zend_always_inline bool zend_call_stack_overflowed(void *stack_limit) { + return (uintptr_t) zend_call_stack_position() <= (uintptr_t) stack_limit; + +} + +static inline void* zend_call_stack_limit(void *base, size_t size, size_t reserved_size) +{ + if (UNEXPECTED(size > (uintptr_t)base)) { + return (void*)0; + } + + base = (int8_t*)base - size; + + if (UNEXPECTED(UINTPTR_MAX - (uintptr_t)base < reserved_size)) { + return (void*)UINTPTR_MAX; + } + + return (int8_t*)base + reserved_size; +} + +static inline size_t zend_call_stack_default_size(void) +{ +#ifdef __linux__ + return 8 * 1024 * 1024; +#endif +#ifdef __FreeBSD__ + // TODO + return 8 * 1024 * 1024; +#endif +#ifdef ZEND_WIN32 + // TODO + // General + // https://docs.microsoft.com/en-us/windows/win32/procthread/thread-stack-size + return 1 * 1024 * 1024; + // IIS + // https://docs.microsoft.com/en-us/troubleshoot/developer/webapps/iis/site-behavior-performance/default-maximum-stack-size-thread + return 256 * 1024; +#endif +#ifdef __APPLE__ + // https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html + if (pthread_main_np()) { + return 512 * 1024; + } + return 8 * 1024 * 1024; +#endif + + return 2 * 1024 * 1024; +} + +#endif /* ZEND_CALL_STACK_H */ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8e8f59140b554..e73abf2e46c8b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -34,6 +34,7 @@ #include "zend_vm.h" #include "zend_enum.h" #include "zend_observer.h" +#include "zend_call_stack.h" #define SET_NODE(target, src) do { \ target ## _type = (src)->op_type; \ @@ -10310,6 +10311,13 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */ static void zend_compile_expr(znode *result, zend_ast *ast) { +#ifdef ZEND_CHECK_STACK_LIMIT + if (zend_call_stack_overflowed(EG(stack_limit))) { + zend_error_noreturn(E_COMPILE_ERROR, + "Maximum call stack size reached. Try splitting expression"); + } +#endif /* ZEND_CHECK_STACK_LIMIT */ + uint32_t checkpoint = zend_short_circuiting_checkpoint(); zend_compile_expr_inner(result, ast); zend_short_circuiting_commit(checkpoint, result, ast); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4456af19d8df4..cb3f652ab8c5c 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -42,6 +42,7 @@ #include "zend_smart_str.h" #include "zend_observer.h" #include "zend_system_id.h" +#include "zend_call_stack.h" #include "Optimizer/zend_func_info.h" /* Virtual current working directory support */ @@ -2228,6 +2229,13 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_new_element_for_s zend_throw_error(NULL, "[] operator not supported for strings"); } +#ifdef ZEND_CHECK_STACK_LIMIT +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_call_stack_size_error(void) +{ + zend_throw_error(NULL, "Maximum call stack size reached. Infinite recursion?"); +} +#endif /* ZEND_CHECK_STACK_LIMIT */ + static ZEND_COLD void zend_binary_assign_op_dim_slow(zval *container, zval *dim OPLINE_DC EXECUTE_DATA_DC) { if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 3e19c0cfa8ee9..8f74cd1a433cb 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -37,6 +37,7 @@ #include "zend_weakrefs.h" #include "zend_inheritance.h" #include "zend_observer.h" +#include "zend_call_stack.h" #ifdef HAVE_SYS_TIME_H #include #endif @@ -171,6 +172,27 @@ void init_executor(void) /* {{{ */ ZEND_ATOMIC_BOOL_INIT(&EG(vm_interrupt), false); ZEND_ATOMIC_BOOL_INIT(&EG(timed_out), false); +#ifdef ZEND_CHECK_STACK_LIMIT + if (EXPECTED(EG(max_allowed_stack_size) == ZEND_MAX_ALLOWED_STACK_USAGE_DETECT)) { + void *base = EG(call_stack).base; + size_t size = EG(call_stack).max_size; + if (UNEXPECTED(base == (void*)0)) { + base = zend_call_stack_position(); + size = zend_call_stack_default_size(); + } + EG(stack_limit) = zend_call_stack_limit(base, size, EG(reserved_stack_size)); + } else if (EG(max_allowed_stack_size) == ZEND_MAX_ALLOWED_STACK_USAGE_UNCHECKED) { + EG(stack_limit) = (void*)0; + } else { + ZEND_ASSERT(EG(max_allowed_stack_size) > 0); + void *base = EG(call_stack).base; + if (UNEXPECTED(base == (void*)0)) { + base = zend_call_stack_position(); + } + EG(stack_limit) = zend_call_stack_limit(base, EG(max_allowed_stack_size), EG(reserved_stack_size)); + } +#endif /* ZEND_CHECK_STACK_LIMIT */ + EG(exception) = NULL; EG(prev_exception) = NULL; diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 2df1f0917b32e..9cde0b043ac47 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -94,6 +94,9 @@ typedef struct _zend_fiber_vm_state { uint32_t jit_trace_num; JMP_BUF *bailout; zend_fiber *active_fiber; +#ifdef ZEND_CHECK_STACK_LIMIT + void *stack_limit; +#endif } zend_fiber_vm_state; static zend_always_inline void zend_fiber_capture_vm_state(zend_fiber_vm_state *state) @@ -107,6 +110,9 @@ static zend_always_inline void zend_fiber_capture_vm_state(zend_fiber_vm_state * state->jit_trace_num = EG(jit_trace_num); state->bailout = EG(bailout); state->active_fiber = EG(active_fiber); +#ifdef ZEND_CHECK_STACK_LIMIT + state->stack_limit = EG(stack_limit); +#endif } static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state *state) @@ -120,6 +126,9 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state * EG(jit_trace_num) = state->jit_trace_num; EG(bailout) = state->bailout; EG(active_fiber) = state->active_fiber; +#ifdef ZEND_CHECK_STACK_LIMIT + EG(stack_limit) = state->stack_limit; +#endif } #ifdef ZEND_FIBER_UCONTEXT @@ -259,6 +268,15 @@ static void zend_fiber_stack_free(zend_fiber_stack *stack) efree(stack); } + +#ifdef ZEND_CHECK_STACK_LIMIT +ZEND_API void* zend_fiber_stack_limit(zend_fiber_stack *stack) +{ + /* stack->pointer is the end of the stack */ + return (int8_t*)stack->pointer + EG(reserved_stack_size); +} +#endif + #ifdef ZEND_FIBER_UCONTEXT static ZEND_NORETURN void zend_fiber_trampoline(void) #else @@ -493,6 +511,10 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) EG(jit_trace_num) = 0; EG(error_reporting) = error_reporting; +#ifdef ZEND_CHECK_STACK_LIMIT + EG(stack_limit) = zend_fiber_stack_limit(fiber->context.stack); +#endif + fiber->fci.retval = &fiber->result; zend_call_function(&fiber->fci, &fiber->fci_cache); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 2673e7814b093..ae05ced4f3625 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -136,6 +136,7 @@ struct _zend_fiber { ZEND_API bool zend_fiber_init_context(zend_fiber_context *context, void *kind, zend_fiber_coroutine coroutine, size_t stack_size); ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context); ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer); +ZEND_API void* zend_fiber_stack_limit(zend_fiber_stack *stack); ZEND_API void zend_fiber_switch_block(void); ZEND_API void zend_fiber_switch_unblock(void); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 17469fab0c11e..155c8a49e9fc5 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -37,6 +37,7 @@ #include "zend_multibyte.h" #include "zend_multiply.h" #include "zend_arena.h" +#include "zend_call_stack.h" /* Define ZTS if you want a thread-safe Zend */ /*#undef ZTS*/ @@ -54,6 +55,10 @@ END_EXTERN_C() #define SYMTABLE_CACHE_SIZE 32 +#ifdef ZEND_CHECK_STACK_LIMIT +# define ZEND_MAX_ALLOWED_STACK_USAGE_UNCHECKED -1 +# define ZEND_MAX_ALLOWED_STACK_USAGE_DETECT 0 +#endif #include "zend_compile.h" @@ -193,6 +198,7 @@ struct _zend_executor_globals { zend_atomic_bool vm_interrupt; zend_atomic_bool timed_out; zend_long hard_timeout; + void *stack_limit; #ifdef ZEND_WIN32 OSVERSIONINFOEX windows_version_info; @@ -271,6 +277,12 @@ struct _zend_executor_globals { zend_string *filename_override; zend_long lineno_override; +#ifdef ZEND_CHECK_STACK_LIMIT + zend_call_stack call_stack; + zend_long max_allowed_stack_size; + zend_ulong reserved_stack_size; +#endif + void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 56904b0162273..0ef05efc4178d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7599,7 +7599,7 @@ ZEND_VM_HOT_NOCONST_HANDLER(198, ZEND_JMP_NULL, CONST|TMP|VAR|CV, JMP_ADDR) uint32_t short_circuiting_type = opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK; if (EXPECTED(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { ZVAL_NULL(result); - if (OP1_TYPE == IS_CV + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF) && (opline->extended_value & ZEND_JMP_NULL_BP_VAR_IS) == 0 ) { @@ -7911,6 +7911,12 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) { const zend_op *throw_op = EG(opline_before_exception); + + /* Exception was thrown before executing any op */ + if (UNEXPECTED(!throw_op)) { + ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, -1, 0, 0); + } + uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes; int i, current_try_catch_offset = -1; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index beb03114a5b61..95ff51c111289 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3164,6 +3164,12 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { const zend_op *throw_op = EG(opline_before_exception); + + /* Exception was thrown before executing any op */ + if (UNEXPECTED(!throw_op)) { + ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(-1, 0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC)); + } + uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes; int i, current_try_catch_offset = -1; @@ -55779,6 +55785,16 @@ ZEND_API void execute_ex(zend_execute_data *ex) LOAD_OPLINE(); ZEND_VM_LOOP_INTERRUPT_CHECK(); +#ifdef ZEND_CHECK_STACK_LIMIT + if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) { + zend_call_stack_size_error(); + /* No opline was executed before exception */ + EG(opline_before_exception) = NULL; + LOAD_OPLINE(); + /* Fall through to handle exception below. */ + } +#endif /* ZEND_CHECK_STACK_LIMIT */ + while (1) { #if !defined(ZEND_VM_FP_GLOBAL_REG) || !defined(ZEND_VM_IP_GLOBAL_REG) int ret; diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 582ec490e2d77..a131eead57205 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -16,6 +16,16 @@ ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *ex) LOAD_OPLINE(); ZEND_VM_LOOP_INTERRUPT_CHECK(); +#ifdef ZEND_CHECK_STACK_LIMIT + if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) { + zend_call_stack_size_error(); + /* No opline was executed before exception */ + EG(opline_before_exception) = NULL; + LOAD_OPLINE(); + /* Fall through to handle exception below. */ + } +#endif /* ZEND_CHECK_STACK_LIMIT */ + while (1) { {%ZEND_VM_CONTINUE_LABEL%} {%ZEND_VM_DISPATCH%} { diff --git a/build/php.m4 b/build/php.m4 index e4bef9d9f2d14..34652da7d8d29 100644 --- a/build/php.m4 +++ b/build/php.m4 @@ -2733,6 +2733,26 @@ AC_DEFUN([PHP_CHECK_BUILTIN_CPU_SUPPORTS], [ [$have_builtin_cpu_supports], [Whether the compiler supports __builtin_cpu_supports]) ]) +dnl +dnl PHP_CHECK_BUILTIN_FRAME_ADDRESS +dnl +AC_DEFUN([PHP_CHECK_BUILTIN_FRAME_ADDRESS], [ + AC_MSG_CHECKING([for __builtin_frame_address]) + + AC_LINK_IFELSE([AC_LANG_PROGRAM([], [[ + return __builtin_frame_address(0) != (void*)0; + ]])], [ + have_builtin_frame_address=1 + AC_MSG_RESULT([yes]) + ], [ + have_builtin_frame_address=0 + AC_MSG_RESULT([no]) + ]) + + AC_DEFINE_UNQUOTED([PHP_HAVE_BUILTIN_FRAME_ADDRESS], + [$have_builtin_frame_address], [Whether the compiler supports __builtin_frame_address]) +]) + dnl dnl PHP_PATCH_CONFIG_HEADERS([FILE]) dnl diff --git a/configure.ac b/configure.ac index 91df09d1ff19e..8bf9f5099054f 100644 --- a/configure.ac +++ b/configure.ac @@ -510,6 +510,8 @@ dnl Check __builtin_cpu_init PHP_CHECK_BUILTIN_CPU_INIT dnl Check __builtin_cpu_supports PHP_CHECK_BUILTIN_CPU_SUPPORTS +dnl Check __builtin_frame_address +PHP_CHECK_BUILTIN_FRAME_ADDRESS dnl Check prctl PHP_CHECK_PRCTL dnl Check procctl @@ -1637,7 +1639,7 @@ PHP_ADD_SOURCES_X(/main, internal_functions_cli.c, -DZEND_ENABLE_STATIC_TSRMLS_C PHP_ADD_SOURCES(Zend, \ zend_language_parser.c zend_language_scanner.c \ zend_ini_parser.c zend_ini_scanner.c \ - zend_alloc.c zend_compile.c zend_constants.c zend_dtrace.c \ + zend_alloc.c zend_call_stack.c zend_compile.c zend_constants.c zend_dtrace.c \ zend_execute_API.c zend_highlight.c zend_llist.c \ zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \ zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \ diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 68a820bd212ac..6d8cca0961e48 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -21,7 +21,7 @@ static ZEND_COLD void undef_result_after_exception(void) { const zend_op *opline = EG(opline_before_exception); ZEND_ASSERT(EG(exception)); - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + if (opline && opline->result_type & (IS_VAR | IS_TMP_VAR)) { zend_execute_data *execute_data = EG(current_execute_data); ZVAL_UNDEF(EX_VAR(opline->result.var)); } @@ -785,7 +785,7 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_rw_helper(zend_array *ht, zval *di if (UNEXPECTED(opline->opcode == ZEND_HANDLE_EXCEPTION)) { opline = EG(opline_before_exception); } - if (!zend_jit_undefined_op_helper_write(ht, opline->op2.var)) { + if (opline && !zend_jit_undefined_op_helper_write(ht, opline->op2.var)) { if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -1003,7 +1003,8 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim default: zend_jit_illegal_offset(); undef_result_after_exception(); - if ((EG(opline_before_exception)+1)->opcode == ZEND_OP_DATA + if (EG(opline_before_exception) + && (EG(opline_before_exception)+1)->opcode == ZEND_OP_DATA && ((EG(opline_before_exception)+1)->op1_type & (IS_VAR|IS_TMP_VAR))) { zend_execute_data *execute_data = EG(current_execute_data); diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 5342731fc81b7..8e02fbbbfeac2 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -183,7 +183,7 @@ bool ZEND_FASTCALL zend_jit_deprecated_helper(OPLINE_D) zend_execute_data *execute_data = EG(current_execute_data); #endif const zend_op *opline = EG(opline_before_exception); - if (RETURN_VALUE_USED(opline)) { + if (opline && RETURN_VALUE_USED(opline)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); } diff --git a/ext/zend_test/fiber.c b/ext/zend_test/fiber.c index f67c0217d050f..2a0879492120e 100644 --- a/ext/zend_test/fiber.c +++ b/ext/zend_test/fiber.c @@ -95,6 +95,9 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran EG(current_execute_data) = execute_data; EG(jit_trace_num) = 0; +#ifdef ZEND_CHECK_STACK_LIMIT + EG(stack_limit) = zend_fiber_stack_limit(fiber->context.stack); +#endif fiber->fci.retval = &retval; zend_call_function(&fiber->fci, &fiber->fci_cache); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 27e5a2a69a8c2..1db8dbbf06684 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -30,6 +30,7 @@ #include "zend_weakrefs.h" #include "Zend/Optimizer/zend_optimizer.h" #include "test_arginfo.h" +#include "zend_call_stack.h" ZEND_DECLARE_MODULE_GLOBALS(zend_test) @@ -455,6 +456,59 @@ static ZEND_FUNCTION(zend_test_parameter_with_attribute) RETURN_LONG(1); } +static ZEND_FUNCTION(zend_test_zend_call_stack_get) +{ + zend_call_stack stack; + + ZEND_PARSE_PARAMETERS_NONE(); + + if (zend_call_stack_get(&stack)) { + zend_string *str; + + array_init(return_value); + + str = strpprintf(0, "%p", stack.base); + add_assoc_str(return_value, "base", str); + + str = strpprintf(0, "0x%zx", stack.max_size); + add_assoc_str(return_value, "max_size", str); + + str = strpprintf(0, "%p", zend_call_stack_position()); + add_assoc_str(return_value, "position", str); + + str = strpprintf(0, "%p", EG(stack_limit)); + add_assoc_str(return_value, "EG(stack_limit)", str); + + return; + } + + RETURN_NULL(); +} + +static zend_long zend_call_stack_use_all(void *limit, zend_long depth) +{ + if (zend_call_stack_overflowed(limit)) { + return depth; + } + + return zend_call_stack_use_all(limit, depth+1); +} + +static ZEND_FUNCTION(zend_test_zend_call_stack_use_all) +{ + zend_call_stack stack; + + ZEND_PARSE_PARAMETERS_NONE(); + + if (!zend_call_stack_get(&stack)) { + return; + } + + void *limit = zend_call_stack_limit(stack.base, stack.max_size, 4096); + + RETURN_LONG(zend_call_stack_use_all(limit, 0)); +} + static zend_object *zend_test_class_new(zend_class_entry *class_type) { zend_object *obj = zend_objects_new(class_type); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 47442dcaab48d..2e3f61e49a6c2 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -154,6 +154,9 @@ function zend_test_zend_ini_parse_quantity(string $str): int {} function zend_test_zend_ini_parse_uquantity(string $str): int {} function zend_test_zend_ini_str(): string {} + + function zend_test_zend_call_stack_get(): ?array {} + function zend_test_zend_call_stack_use_all(): int {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 84b73abbc5516..097bc45a61072 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d8c0689141c897c3f31d00550128df7b8c00274f */ + * Stub hash: ba202acf6108492fc94cbc473c0e262d2c85ad12 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -97,6 +97,11 @@ ZEND_END_ARG_INFO() #define arginfo_zend_test_zend_ini_str arginfo_zend_get_current_func_name +#define arginfo_zend_test_zend_call_stack_get arginfo_zend_test_nullable_array_return + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_zend_call_stack_use_all, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_namespaced_func, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -114,8 +119,7 @@ ZEND_END_ARG_INFO() #define arginfo_ZendTestNS2_ZendSubNS_namespaced_deprecated_aliased_func arginfo_zend_test_void_return -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_is_object, 0, 0, IS_LONG, 0) -ZEND_END_ARG_INFO() +#define arginfo_class__ZendTestClass_is_object arginfo_zend_test_zend_call_stack_use_all #define arginfo_class__ZendTestClass___toString arginfo_zend_get_current_func_name @@ -183,6 +187,8 @@ static ZEND_FUNCTION(zend_call_method); static ZEND_FUNCTION(zend_test_zend_ini_parse_quantity); static ZEND_FUNCTION(zend_test_zend_ini_parse_uquantity); static ZEND_FUNCTION(zend_test_zend_ini_str); +static ZEND_FUNCTION(zend_test_zend_call_stack_get); +static ZEND_FUNCTION(zend_test_zend_call_stack_use_all); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -232,6 +238,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_zend_ini_parse_quantity, arginfo_zend_test_zend_ini_parse_quantity) ZEND_FE(zend_test_zend_ini_parse_uquantity, arginfo_zend_test_zend_ini_parse_uquantity) ZEND_FE(zend_test_zend_ini_str, arginfo_zend_test_zend_ini_str) + ZEND_FE(zend_test_zend_call_stack_get, arginfo_zend_test_zend_call_stack_get) + ZEND_FE(zend_test_zend_call_stack_use_all, arginfo_zend_test_zend_call_stack_use_all) ZEND_NS_FALIAS("ZendTestNS2", namespaced_func, ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func) ZEND_NS_DEP_FALIAS("ZendTestNS2", namespaced_deprecated_func, ZendTestNS2_namespaced_deprecated_func, arginfo_ZendTestNS2_namespaced_deprecated_func) ZEND_NS_FALIAS("ZendTestNS2", namespaced_aliased_func, zend_test_void_return, arginfo_ZendTestNS2_namespaced_aliased_func) diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index 344b9c73e476d..b381f574fc10a 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -611,7 +611,7 @@ PHPDBG_API bool phpdbg_check_caught_ex(zend_execute_data *execute_data, zend_obj uint32_t op_num, i; zend_op_array *op_array = &execute_data->func->op_array; - if (execute_data->opline >= EG(exception_op) && execute_data->opline < EG(exception_op) + 3) { + if (execute_data->opline >= EG(exception_op) && execute_data->opline < EG(exception_op) + 3 && EG(opline_before_exception)) { op = EG(opline_before_exception); } else { op = execute_data->opline; diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 0b1736c8ab70d..20fcf2409922f 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -228,8 +228,8 @@ STDOUT.WriteLine("Build dir: " + get_define('BUILD_DIR')); STDOUT.WriteLine("PHP Core: " + get_define('PHPDLL') + " and " + get_define('PHPLIB')); ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ - zend_ini_parser.c zend_ini_scanner.c zend_alloc.c zend_compile.c \ - zend_constants.c zend_exceptions.c \ + zend_ini_parser.c zend_ini_scanner.c zend_alloc.c zend_call_stack.c \ + zend_compile.c zend_constants.c zend_exceptions.c \ zend_execute_API.c zend_highlight.c \ zend_llist.c zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c \ zend_stack.c zend_variables.c zend.c zend_API.c zend_extensions.c \ @@ -303,6 +303,9 @@ if (VS_TOOLSET && VCVERS >= 1914) { AC_DEFINE('HAVE_STRNLEN', 1); +AC_DEFINE('ZEND_STACK_GROWS_DOWNWARDS', 1) +AC_DEFINE('ZEND_CHECK_STACK_LIMIT', 1) + ADD_SOURCES("main/streams", "streams.c cast.c memory.c filter.c plain_wrapper.c \ userspace.c transports.c xp_socket.c mmap.c glob_wrapper.c"); ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");