From 27ae51d089cb4dc23d12834a39393acfaed9b68f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 2 Jun 2026 13:01:01 +0200 Subject: [PATCH 1/3] win32/signal.c: convert ctrl_handler to FCC --- .../sapi_windows_set_ctrl_handler_leak.phpt | 2 +- .../sapi_windows_set_ctrl_trampoline.phpt | 31 +++++++++++++++ win32/signal.c | 38 ++++++++++--------- 3 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 sapi/cli/tests/sapi_windows_set_ctrl_trampoline.phpt diff --git a/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt b/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt index e91b6c63c344..b695e5c013b5 100644 --- a/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt +++ b/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt @@ -1,5 +1,5 @@ --TEST-- -sapi_windows_set_ctrl_handler() leak bug +sapi_windows_set_ctrl_handler() trampoline test --SKIPIF-- +--FILE-- + +--EXPECT-- +Done diff --git a/win32/signal.c b/win32/signal.c index c04fe860437d..a55b24860e30 100644 --- a/win32/signal.c +++ b/win32/signal.c @@ -18,7 +18,7 @@ #include "win32/console.h" /* true globals; only used from main thread and from kernel callback */ -static zval ctrl_handler; +static zend_fcall_info_cache ctrl_handler; static DWORD ctrl_evt = (DWORD)-1; static zend_atomic_bool *vm_interrupt_flag = NULL; @@ -26,14 +26,12 @@ static void (*orig_interrupt_function)(zend_execute_data *execute_data); static void php_win32_signal_ctrl_interrupt_function(zend_execute_data *execute_data) {/*{{{*/ - if (IS_UNDEF != Z_TYPE(ctrl_handler)) { - zval retval, params[1]; + if (ZEND_FCC_INITIALIZED(ctrl_handler)) { + zval params[1]; ZVAL_LONG(¶ms[0], ctrl_evt); - /* If the function returns, */ - call_user_function(NULL, NULL, &ctrl_handler, &retval, 1, params); - zval_ptr_dtor(&retval); + zend_call_known_fcc(&ctrl_handler, NULL, 1, params, NULL); } if (orig_interrupt_function) { @@ -51,7 +49,7 @@ PHP_WINUTIL_API void php_win32_signal_ctrl_handler_init(void) orig_interrupt_function = zend_interrupt_function; zend_interrupt_function = php_win32_signal_ctrl_interrupt_function; vm_interrupt_flag = &EG(vm_interrupt); - ZVAL_UNDEF(&ctrl_handler); + ctrl_handler = empty_fcall_info_cache; REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_C", CTRL_C_EVENT, CONST_PERSISTENT); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_BREAK", CTRL_BREAK_EVENT, CONST_PERSISTENT); @@ -82,9 +80,8 @@ PHP_WINUTIL_API void php_win32_signal_ctrl_handler_request_shutdown(void) /* The ctrl_handler must be cleared between requests, otherwise we can crash * due to accessing a previous request's memory. */ - if (!Z_ISUNDEF(ctrl_handler)) { - zval_ptr_dtor(&ctrl_handler); - ZVAL_UNDEF(&ctrl_handler); + if (ZEND_FCC_INITIALIZED(ctrl_handler)) { + zend_fcc_dtor(&ctrl_handler); } } @@ -110,12 +107,15 @@ PHP_FUNCTION(sapi_windows_set_ctrl_handler) /* callable argument corresponds to the CTRL handler */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!|b", &fci, &fcc, &add) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "F!|b", &fci, &fcc, &add) == FAILURE) { RETURN_THROWS(); } #ifdef ZTS if (!tsrm_is_main_thread()) { + if (ZEND_FCC_INITIALIZED(fcc)) { + zend_release_fcall_info_cache(&fcc); + } zend_throw_error(NULL, "CTRL events can only be received on the main thread"); RETURN_THROWS(); } @@ -126,9 +126,10 @@ PHP_FUNCTION(sapi_windows_set_ctrl_handler) RETURN_THROWS(); } - if (!ZEND_FCI_INITIALIZED(fci)) { - zval_ptr_dtor(&ctrl_handler); - ZVAL_UNDEF(&ctrl_handler); + if (!ZEND_FCC_INITIALIZED(fcc)) { + if (ZEND_FCC_INITIALIZED(ctrl_handler)) { + zend_fcc_dtor(&ctrl_handler); + } RETURN_BOOL(SetConsoleCtrlHandler(NULL, add)); } @@ -136,11 +137,14 @@ PHP_FUNCTION(sapi_windows_set_ctrl_handler) zend_string *func_name = zend_get_callable_name(&fci.function_name); php_error_docref(NULL, E_WARNING, "Unable to attach %s as a CTRL handler", ZSTR_VAL(func_name)); zend_string_release_ex(func_name, 0); + zend_release_fcall_info_cache(&fcc); RETURN_FALSE; } - zval_ptr_dtor(&ctrl_handler); - ZVAL_COPY(&ctrl_handler, &fci.function_name); + if (ZEND_FCC_INITIALIZED(ctrl_handler)) { + zend_fcc_dtor(&ctrl_handler); + } + zend_fcc_dup(&ctrl_handler, &fcc); RETURN_TRUE; }/*}}}*/ @@ -163,7 +167,7 @@ PHP_FUNCTION(sapi_windows_generate_ctrl_event) ret = (GenerateConsoleCtrlEvent(evt, pid) != 0); - if (IS_UNDEF != Z_TYPE(ctrl_handler)) { + if (ZEND_FCC_INITIALIZED(ctrl_handler)) { ret = ret && SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, TRUE); } From 19084180e402befd2ab99d4e100dbfb76e39f1bf Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 2 Jun 2026 13:19:30 +0200 Subject: [PATCH 2/3] fix tests description --- sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt | 2 +- sapi/cli/tests/sapi_windows_set_ctrl_trampoline.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt b/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt index b695e5c013b5..e91b6c63c344 100644 --- a/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt +++ b/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt @@ -1,5 +1,5 @@ --TEST-- -sapi_windows_set_ctrl_handler() trampoline test +sapi_windows_set_ctrl_handler() leak bug --SKIPIF-- Date: Tue, 2 Jun 2026 20:40:24 +0200 Subject: [PATCH 3/3] Free trampoline in another error path --- win32/signal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/win32/signal.c b/win32/signal.c index a55b24860e30..abce3edb2fd1 100644 --- a/win32/signal.c +++ b/win32/signal.c @@ -122,6 +122,9 @@ PHP_FUNCTION(sapi_windows_set_ctrl_handler) #endif if (!php_win32_console_is_cli_sapi()) { + if (ZEND_FCC_INITIALIZED(fcc)) { + zend_release_fcall_info_cache(&fcc); + } zend_throw_error(NULL, "CTRL events trapping is only supported on console"); RETURN_THROWS(); }