From 21bcabda50ba326ab9f962b8fae0040938707b02 Mon Sep 17 00:00:00 2001 From: Sammy Kaye Powers Date: Fri, 11 Sep 2020 15:24:06 -0700 Subject: [PATCH 1/5] Add observer tests for call_user_func() and friend --- .../tests/observer_call_user_func_01.phpt | 40 +++++++++++++++++++ .../tests/observer_call_user_func_02.phpt | 40 +++++++++++++++++++ .../tests/observer_call_user_func_03.phpt | 39 ++++++++++++++++++ .../tests/observer_call_user_func_04.phpt | 39 ++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 ext/zend_test/tests/observer_call_user_func_01.phpt create mode 100644 ext/zend_test/tests/observer_call_user_func_02.phpt create mode 100644 ext/zend_test/tests/observer_call_user_func_03.phpt create mode 100644 ext/zend_test/tests/observer_call_user_func_04.phpt diff --git a/ext/zend_test/tests/observer_call_user_func_01.phpt b/ext/zend_test/tests/observer_call_user_func_01.phpt new file mode 100644 index 0000000000000..0f12fa6835f00 --- /dev/null +++ b/ext/zend_test/tests/observer_call_user_func_01.phpt @@ -0,0 +1,40 @@ +--TEST-- +Observer: call_user_func() from root namespace +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +--FILE-- + +--EXPECTF-- + + + + +MyClass::myMethod called + + + +my_function called + + diff --git a/ext/zend_test/tests/observer_call_user_func_02.phpt b/ext/zend_test/tests/observer_call_user_func_02.phpt new file mode 100644 index 0000000000000..28dd6a9825440 --- /dev/null +++ b/ext/zend_test/tests/observer_call_user_func_02.phpt @@ -0,0 +1,40 @@ +--TEST-- +Observer: call_user_func_array() from root namespace +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +--FILE-- + +--EXPECTF-- + + + + +MyClass::myMethod called + + + +my_function called + + diff --git a/ext/zend_test/tests/observer_call_user_func_03.phpt b/ext/zend_test/tests/observer_call_user_func_03.phpt new file mode 100644 index 0000000000000..1ff841d4340d5 --- /dev/null +++ b/ext/zend_test/tests/observer_call_user_func_03.phpt @@ -0,0 +1,39 @@ +--TEST-- +Observer: call_user_func() from namespace +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +--FILE-- + +--EXPECTF-- + + + + +MyClass::myMethod called + + + +my_function called + + diff --git a/ext/zend_test/tests/observer_call_user_func_04.phpt b/ext/zend_test/tests/observer_call_user_func_04.phpt new file mode 100644 index 0000000000000..9df131db4dd0d --- /dev/null +++ b/ext/zend_test/tests/observer_call_user_func_04.phpt @@ -0,0 +1,39 @@ +--TEST-- +Observer: call_user_func_array() from namespace +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +--FILE-- + +--EXPECTF-- + + + + +MyClass::myMethod called + + + +my_function called + + From 322798fa174bc85b5ba4cc5f4847eb9dcaf5db32 Mon Sep 17 00:00:00 2001 From: Sammy Kaye Powers Date: Fri, 11 Sep 2020 16:19:11 -0700 Subject: [PATCH 2/5] Add observer test for generator with uncaught exception --- .../tests/observer_generator_05.phpt | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 ext/zend_test/tests/observer_generator_05.phpt diff --git a/ext/zend_test/tests/observer_generator_05.phpt b/ext/zend_test/tests/observer_generator_05.phpt new file mode 100644 index 0000000000000..d13f6fa39b198 --- /dev/null +++ b/ext/zend_test/tests/observer_generator_05.phpt @@ -0,0 +1,53 @@ +--TEST-- +Observer: Generator with uncaught exception +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- + +--EXPECTF-- + + + + + + + +0 + + +1 + + + + + + + + +Fatal error: Uncaught RuntimeException: Oops! in %s/observer_generator_%d.php:%d +Stack trace: +#0 %s/observer_generator_%d.php(%d): fooResults() +#1 %s/observer_generator_%d.php(%d): doSomething() +#2 {main} + thrown in %s/observer_generator_%d.php on line %d From 2612448cefa8edf11dee24ef637a3366686b2381 Mon Sep 17 00:00:00 2001 From: Sammy Kaye Powers Date: Fri, 11 Sep 2020 16:19:59 -0700 Subject: [PATCH 3/5] Add observer support for fatal errors --- Zend/zend.c | 10 ++++++ ext/zend_test/tests/observer_error_01.phpt | 29 ++++++++++++++++ ext/zend_test/tests/observer_error_02.phpt | 28 ++++++++++++++++ ext/zend_test/tests/observer_error_03.phpt | 39 ++++++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 ext/zend_test/tests/observer_error_01.phpt create mode 100644 ext/zend_test/tests/observer_error_02.phpt create mode 100644 ext/zend_test/tests/observer_error_03.phpt diff --git a/Zend/zend.c b/Zend/zend.c index cbd5aef42b9fe..eab0a221f1abd 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1323,6 +1323,16 @@ static ZEND_COLD void zend_error_impl( zend_observer_error_notify(type, error_filename, error_lineno, message); + /* Call all the observer end handlers for fatal errors */ + if (ZEND_OBSERVER_ENABLED && (type & E_FATAL_ERRORS) && EG(current_execute_data)) { + zend_execute_data *ex = EG(current_execute_data); + do { + if (ex->func->type != ZEND_INTERNAL_FUNCTION) { + zend_observer_fcall_end(ex, NULL); + } + } while ((ex = ex->prev_execute_data) != NULL); + } + /* if we don't have a user defined error handler */ if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF || !(EG(user_error_handler_error_reporting) & type) diff --git a/ext/zend_test/tests/observer_error_01.phpt b/ext/zend_test/tests/observer_error_01.phpt new file mode 100644 index 0000000000000..08de38f601156 --- /dev/null +++ b/ext/zend_test/tests/observer_error_01.phpt @@ -0,0 +1,29 @@ +--TEST-- +Observer: End handlers fire after a fatal error +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +memory_limit=1M +--FILE-- + +--EXPECTF-- + + + + + + + +Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/ext/zend_test/tests/observer_error_02.phpt b/ext/zend_test/tests/observer_error_02.phpt new file mode 100644 index 0000000000000..70e2a57a50f9c --- /dev/null +++ b/ext/zend_test/tests/observer_error_02.phpt @@ -0,0 +1,28 @@ +--TEST-- +Observer: End handlers fire after a userland fatal error +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- + +--EXPECTF-- + + + + + + + +Fatal error: Foo error in %s on line %d diff --git a/ext/zend_test/tests/observer_error_03.phpt b/ext/zend_test/tests/observer_error_03.phpt new file mode 100644 index 0000000000000..3d8150a440754 --- /dev/null +++ b/ext/zend_test/tests/observer_error_03.phpt @@ -0,0 +1,39 @@ +--TEST-- +Observer: non-fatal errors do not fire end handlers prematurely +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- + +--EXPECTF-- + + + +
+ + + +Warning: Undefined variable $this_does_not_exit in %s on line %d + +After error. + +Done. + From 687b7c150eee56994f01f2fed3bf1ecacaac1fdd Mon Sep 17 00:00:00 2001 From: Sammy Kaye Powers Date: Fri, 25 Sep 2020 13:33:01 -0700 Subject: [PATCH 4/5] Ensure end handlers fire only once for fatals --- Zend/zend.c | 5 +++ Zend/zend_observer.c | 9 +++++ Zend/zend_observer.h | 2 + ext/zend_test/tests/observer_error_04.phpt | 43 ++++++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 ext/zend_test/tests/observer_error_04.phpt diff --git a/Zend/zend.c b/Zend/zend.c index eab0a221f1abd..4916c6bb05994 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1329,6 +1329,11 @@ static ZEND_COLD void zend_error_impl( do { if (ex->func->type != ZEND_INTERNAL_FUNCTION) { zend_observer_fcall_end(ex, NULL); + /* If an extension catches a fatal error and raises another one, the + * observer end handlers will fire more than once. This requires us + * to "unobserve" the functions after the end handlers have fired the + * first time. */ + zend_observer_fcall_unobserve(ex->func); } } while ((ex = ex->prev_execute_data) != NULL); } diff --git a/Zend/zend_observer.c b/Zend/zend_observer.c index 9c2d1cdf51c4e..ac8653f931a9c 100644 --- a/Zend/zend_observer.c +++ b/Zend/zend_observer.c @@ -210,6 +210,15 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end( } } +ZEND_API void zend_observer_fcall_unobserve(zend_function *func) +{ + if (ZEND_OBSERVER_ENABLED + && ZEND_OBSERVABLE_FN(func->common.fn_flags) + && ZEND_OBSERVER_DATA(&func->op_array)) { + ZEND_OBSERVER_DATA(&func->op_array) = ZEND_OBSERVER_NOT_OBSERVED; + } +} + ZEND_API void zend_observer_error_register(zend_observer_error_cb cb) { zend_llist_add_element(&zend_observer_error_callbacks, &cb); diff --git a/Zend/zend_observer.h b/Zend/zend_observer.h index 1d20306a17018..997d8a25dd14c 100644 --- a/Zend/zend_observer.h +++ b/Zend/zend_observer.h @@ -70,6 +70,8 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end( zend_execute_data *execute_data, zval *return_value); +ZEND_API void zend_observer_fcall_unobserve(zend_function *func); + typedef void (*zend_observer_error_cb)(int type, const char *error_filename, uint32_t error_lineno, zend_string *message); ZEND_API void zend_observer_error_register(zend_observer_error_cb callback); diff --git a/ext/zend_test/tests/observer_error_04.phpt b/ext/zend_test/tests/observer_error_04.phpt new file mode 100644 index 0000000000000..7d12f4a50c0da --- /dev/null +++ b/ext/zend_test/tests/observer_error_04.phpt @@ -0,0 +1,43 @@ +--TEST-- +Observer: fatal errors caught with zend_try will fire end handlers once +--SKIPIF-- + + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +getMessage() . PHP_EOL; + } +} + +function main() +{ + foo(); +} + +main(); + +echo 'Done.' . PHP_EOL; +?> +--EXPECTF-- + + + +
+ + + + + +SOAP-ERROR: Parsing WSDL: Couldn't load from 'foo' : failed to load external entity "foo" + +Done. From 2a8ff944d4a2d597251031b503f0c4cabbfe33cc Mon Sep 17 00:00:00 2001 From: Sammy Kaye Powers Date: Fri, 25 Sep 2020 13:49:01 -0700 Subject: [PATCH 5/5] Update test to make sure ZEND_HANDLE_EXCEPTION plays nicely --- ext/zend_test/tests/observer_error_04.phpt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ext/zend_test/tests/observer_error_04.phpt b/ext/zend_test/tests/observer_error_04.phpt index 7d12f4a50c0da..8aea004187e80 100644 --- a/ext/zend_test/tests/observer_error_04.phpt +++ b/ext/zend_test/tests/observer_error_04.phpt @@ -11,12 +11,8 @@ zend_test.observer.show_return_value=1 getMessage() . PHP_EOL; - } + // ext/soap catches a fatal error and then throws an exception + $client = new SoapClient('foo'); } function main() @@ -24,7 +20,12 @@ function main() foo(); } -main(); +// try/catch is on main() to ensure ZEND_HANDLE_EXCEPTION does not fire the end handlers again +try { + main(); +} catch (SoapFault $e) { + echo $e->getMessage() . PHP_EOL; +} echo 'Done.' . PHP_EOL; ?>