From 6f579934f0d4391af0eab961d612929b71320b39 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 19 Dec 2024 19:43:00 +0100 Subject: [PATCH] Add observer temporary to dl'ed functions When observer is enabled, we normally add an extra temporary to all functions, to store the previously observed frame. However, this is done in zend_observer_post_startup() so it doesn't happen to dl'ed() functions. One possible fix would be to move that from zend_observer_post_startup() to zend_register_functions(), but this would be too early: Observer may not be enabled when zend_register_functions() is called, and may still be enabled later. However, when zend_register_functions() is called at run-time (during dl()), we know definitively whether observer is enabled. Here I update zend_register_functions() to add a temporary to dl'ed() functions when observer is enabled. Fixes: GH-17211 Closes: GH-17220 --- NEWS | 3 ++ Zend/zend_API.c | 9 +++- ext/dl_test/dl_test.c | 18 ++++++++ ext/dl_test/dl_test.stub.php | 4 ++ ext/dl_test/dl_test_arginfo.h | 21 +++++++++- .../tests/general_functions/gh17211.phpt | 42 +++++++++++++++++++ 6 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/general_functions/gh17211.phpt diff --git a/NEWS b/NEWS index f7565624ad7b0..7230fc5bd3b6c 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.28 +- Core: + . Fixed bug GH-17211 (observer segfault on function loaded with dl()). + (Arnaud) 19 Dec 2024, PHP 8.2.27 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index eb6e349f7ec21..6aeb2d653faaf 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2718,7 +2718,14 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend } internal_function->type = ZEND_INTERNAL_FUNCTION; internal_function->module = EG(current_module); - internal_function->T = 0; + if (EG(active) && ZEND_OBSERVER_ENABLED) { + /* Add an observer temporary to store previous observed frames. This is + * normally handled by zend_observer_post_startup(), except for + * functions registered at runtime (EG(active)). */ + internal_function->T = 1; + } else { + internal_function->T = 0; + } memset(internal_function->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*)); while (ptr->fname) { diff --git a/ext/dl_test/dl_test.c b/ext/dl_test/dl_test.c index f4a8ab8198ede..a4f42454c565e 100644 --- a/ext/dl_test/dl_test.c +++ b/ext/dl_test/dl_test.c @@ -76,9 +76,27 @@ PHP_INI_BEGIN() PHP_INI_END() /* }}} */ +PHP_METHOD(DlTest, test) +{ + char *var = "World"; + size_t var_len = sizeof("World") - 1; + zend_string *retval; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(var, var_len) + ZEND_PARSE_PARAMETERS_END(); + + retval = strpprintf(0, "Hello %s", var); + + RETURN_STR(retval); +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(dl_test) { + register_class_DlTest(); + /* Test backwards compatibility */ if (getenv("PHP_DL_TEST_USE_OLD_REGISTER_INI_ENTRIES")) { zend_register_ini_entries(ini_entries, module_number); diff --git a/ext/dl_test/dl_test.stub.php b/ext/dl_test/dl_test.stub.php index 524c8206365b8..cd8b3916bae2e 100644 --- a/ext/dl_test/dl_test.stub.php +++ b/ext/dl_test/dl_test.stub.php @@ -8,3 +8,7 @@ function dl_test_test1(): void {} function dl_test_test2(string $str = ""): string {} + +class DlTest { + public function test(string $str = ""): string {} +} diff --git a/ext/dl_test/dl_test_arginfo.h b/ext/dl_test/dl_test_arginfo.h index fdd0e3a89e10e..0618bbdb222ca 100644 --- a/ext/dl_test/dl_test_arginfo.h +++ b/ext/dl_test/dl_test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 547ddbc21e9aa853b491cb17e902bbbb9cc2df00 */ + * Stub hash: 2dbacf5282b0f8e53923ac70495c2da43c7237e3 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test1, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -8,9 +8,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test2, 0, 0, IS_STRING, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, str, IS_STRING, 0, "\"\"") ZEND_END_ARG_INFO() +#define arginfo_class_DlTest_test arginfo_dl_test_test2 + ZEND_FUNCTION(dl_test_test1); ZEND_FUNCTION(dl_test_test2); +ZEND_METHOD(DlTest, test); static const zend_function_entry ext_functions[] = { @@ -18,3 +21,19 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(dl_test_test2, arginfo_dl_test_test2) ZEND_FE_END }; + + +static const zend_function_entry class_DlTest_methods[] = { + ZEND_ME(DlTest, test, arginfo_class_DlTest_test, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_class_entry *register_class_DlTest(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "DlTest", class_DlTest_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + return class_entry; +} diff --git a/ext/standard/tests/general_functions/gh17211.phpt b/ext/standard/tests/general_functions/gh17211.phpt new file mode 100644 index 0000000000000..5df8a6638a677 --- /dev/null +++ b/ext/standard/tests/general_functions/gh17211.phpt @@ -0,0 +1,42 @@ +--TEST-- +dl() / observer segfault +--EXTENSIONS-- +zend_test +--SKIPIF-- + +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.observe_functions=1 +zend_test.observer.show_output=1 +--FILE-- +test("World!")); +?> +--EXPECTF-- + + +
+
+ + + + + +string(12) "Hello World!" + + + + + +string(12) "Hello World!" +