1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-18033: NULL-ptr dereference when using register_tick_function in destructor

The problem is that `php_request_shutdown` calls `php_deactivate_ticks` prior
to running destructors and the shutdown functions and finalizing output
handlers.
So if a destructor or shutdown function re-registers a tick function,
then the user tick functions handler will be added back to `PG(tick_functions)`.
When the next request happens, the list `PG(tick_functions)` still contains an
entry to call the user tick functions (added in the previous request
during shutdown). This causes a NULL deref eventually because
`run_user_tick_functions` assumes that if it is called then
`BG(user_tick_functions)` must be non-NULL.

Fix this by moving the tick handler deactivation.

Closes GH-18047.
This commit is contained in:
Niels Dossche
2025-03-13 19:28:27 +01:00
parent f75dd82866
commit 867ed156f7
5 changed files with 47 additions and 2 deletions

2
NEWS
View File

@@ -33,6 +33,8 @@ PHP NEWS
zend.exception_string_param_max_len=0. (timwolla)
. Fixed bug GH-17959 (Relax missing trait fatal error to error exception).
(ilutov)
. Fixed bug GH-18033 (NULL-ptr dereference when using register_tick_function
in destructor). (nielsdos)
- Curl:
. Added curl_multi_get_handles(). (timwolla)

View File

@@ -38,6 +38,9 @@ PHP 8.5 UPGRADE NOTES
resources that were indirectly collected through cycles.
. It is now allowed to substitute static with self or the concrete class name
in final subclasses.
. The tick handlers are now deactivated after all shutdown functions, destructors
have run and the output handlers have been cleaned up.
This is a consequence of fixing GH-18033.
- Intl:
. The extension now requires at least ICU 57.1.

View File

@@ -0,0 +1,24 @@
--TEST--
GH-18033 (NULL-ptr dereference when using register_tick_function in destructor)
--DESCRIPTION--
Needs --repeat 2 or something similar to reproduce
--CREDITS--
clesmian
--FILE--
<?php
class Foo {
function __destruct() {
declare(ticks=1);
register_tick_function(
function() { }
);
echo "In destructor\n";
}
}
$bar = new Foo;
echo "Done\n";
?>
--EXPECT--
Done
In destructor

View File

@@ -0,0 +1,16 @@
--TEST--
GH-18033 (NULL-ptr dereference when using register_tick_function in ob_start)
--DESCRIPTION--
Needs --repeat 2 or something similar to reproduce
--CREDITS--
clesmian
--FILE--
<?php
ob_start(function() {
declare(ticks=1);
register_tick_function(
function() { }
);
});
?>
--EXPECT--

View File

@@ -1904,8 +1904,6 @@ void php_request_shutdown(void *dummy)
*/
EG(current_execute_data) = NULL;
php_deactivate_ticks();
/* 0. Call any open observer end handlers that are still open after a zend_bailout */
if (ZEND_OBSERVER_ENABLED) {
zend_observer_fcall_end_all();
@@ -1926,6 +1924,8 @@ void php_request_shutdown(void *dummy)
php_output_end_all();
} zend_end_try();
php_deactivate_ticks();
/* 4. Reset max_execution_time (no longer executing php code after response sent) */
zend_try {
zend_unset_timeout();