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.

Closes GH-18231.
This commit is contained in:
Niels Dossche 2025-04-02 20:11:12 +02:00
parent 8a585856d1
commit fb3536fd60
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
5 changed files with 55 additions and 2 deletions

3
NEWS
View file

@ -32,6 +32,9 @@ PHP NEWS
- Standard:
. Fixed bug GH-17403 (Potential deadlock when putenv fails). (nielsdos)
- Windows:
. Fix leak+crash with sapi_windows_set_ctrl_handler(). (nielsdos)
- Zip:
. Fixed bug GH-18431 (Registering ZIP progress callback twice doesn't work).
(nielsdos)

View file

@ -0,0 +1,28 @@
--TEST--
sapi_windows_set_ctrl_handler() leak bug
--SKIPIF--
<?php
include "skipif.inc";
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
die("skip this test is for Windows platforms only");
?>
--FILE--
<?php
class Test {
public function set() {
sapi_windows_set_ctrl_handler(self::cb(...));
}
public function cb() {
}
}
$test = new Test;
$test->set();
echo "Done\n";
?>
--EXPECT--
Done

View file

@ -63,5 +63,7 @@ PHP_RSHUTDOWN_FUNCTION(win32_core_globals)
{/*{{{*/
closelog();
php_win32_signal_ctrl_handler_request_shutdown();
return SUCCESS;
}/*}}}*/

View file

@ -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;

View file

@ -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 */