From 15994629b277171483a4b1a54641e163c7fc3343 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:11:12 +0200 Subject: [PATCH] Fix leak+crash with sapi_windows_set_ctrl_handler() The ctrl_handler is never destroyed. We have to destroy it at request end so we avoid leaking it and also avoid keeping a reference to previous request memory in a next request. The latter can result in a crash and can be demonstrated with this script and `--repeat 2`: ```php class Test { public function set() { sapi_windows_set_ctrl_handler(self::cb(...)); } public function cb() { } } $test = new Test; $test->set(); sleep(3); ``` When you hit CTRL+C in the second request you can crash. This patch resolves both the leak and crash by destroying the ctrl_handler after a request. --- .../sapi_windows_set_ctrl_handler_leak.phpt | 28 +++++++++++++++++++ win32/globals.c | 2 ++ win32/signal.c | 23 +++++++++++++-- win32/signal.h | 1 + 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.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 new file mode 100644 index 0000000000000..e91b6c63c344f --- /dev/null +++ b/sapi/cli/tests/sapi_windows_set_ctrl_handler_leak.phpt @@ -0,0 +1,28 @@ +--TEST-- +sapi_windows_set_ctrl_handler() leak bug +--SKIPIF-- + +--FILE-- +set(); + +echo "Done\n"; + +?> +--EXPECT-- +Done diff --git a/win32/globals.c b/win32/globals.c index b2889c77e9cef..3f35249057328 100644 --- a/win32/globals.c +++ b/win32/globals.c @@ -63,5 +63,7 @@ PHP_RSHUTDOWN_FUNCTION(win32_core_globals) {/*{{{*/ closelog(); + php_win32_signal_ctrl_handler_request_shutdown(); + return SUCCESS; }/*}}}*/ diff --git a/win32/signal.c b/win32/signal.c index 1207fe1fc092f..21b855f02ea2a 100644 --- a/win32/signal.c +++ b/win32/signal.c @@ -68,9 +68,28 @@ PHP_WINUTIL_API void php_win32_signal_ctrl_handler_shutdown(void) zend_interrupt_function = orig_interrupt_function; orig_interrupt_function = NULL; vm_interrupt_flag = NULL; - ZVAL_UNDEF(&ctrl_handler); }/*}}}*/ +PHP_WINUTIL_API void php_win32_signal_ctrl_handler_request_shutdown(void) +{ + /* Must be initialized and in main thread */ + if (!vm_interrupt_flag) { + return; + } +#ifdef ZTS + if (!tsrm_is_main_thread()) { + return; + } +#endif + + /* 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); + } +} + static BOOL WINAPI php_win32_signal_system_ctrl_handler(DWORD evt) {/*{{{*/ if (CTRL_C_EVENT != evt && CTRL_BREAK_EVENT != evt) { @@ -125,7 +144,7 @@ PHP_FUNCTION(sapi_windows_set_ctrl_handler) RETURN_FALSE; } - zval_ptr_dtor_nogc(&ctrl_handler); + zval_ptr_dtor(&ctrl_handler); ZVAL_COPY(&ctrl_handler, &fci.function_name); RETURN_TRUE; diff --git a/win32/signal.h b/win32/signal.h index 2058dd873b075..d7048d96a0a6c 100644 --- a/win32/signal.h +++ b/win32/signal.h @@ -10,6 +10,7 @@ #define SIGPROF 27 /* profiling time alarm */ PHP_WINUTIL_API void php_win32_signal_ctrl_handler_init(void); +PHP_WINUTIL_API void php_win32_signal_ctrl_handler_request_shutdown(void); PHP_WINUTIL_API void php_win32_signal_ctrl_handler_shutdown(void); #endif /* PHP_WIN32_SIGNAL_H */