[php-src] Issue #24350: ext/date: MINIT reads a stale external tz database after an in-process module restart
| From: | TamasSzigeti | Date: | Thu, 15 Oct 2026 13:11:55 +0000 |
| Subject: | [php-src] Issue #24350: ext/date: MINIT reads a stale external tz database after an in-process module restart | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253001@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24350
Author: TamasSzigeti
### Description
PHP_MINIT_FUNCTION(date) calls REGISTER_INI_ENTRIES() before it resets
php_date_global_timezone_db:
```c
PHP_MINIT_FUNCTION(date)
{
REGISTER_INI_ENTRIES(); /* fires OnUpdate_date_timezone("UTC") */
...
php_date_global_timezone_db = NULL;
php_date_global_timezone_db_enabled = 0;
```
OnUpdate_date_timezone validates the value through DATE_TIMEZONEDB, which
still holds whatever the previous
startup left there. When a SAPI shuts the engine down and starts it again in the same process, and a
shared extension
had installed its own database with php_date_set_tzdb() (PECL timezonedb),
that pointer refers to a .so that
module_destructor() has already unloaded. The second startup then reads unmapped
memory: a segfault, or a hang
where the SAPI's signal handling swallows it.
Neither the extension nor date's MSHUTDOWN clears the pointer, and the extension
can't safely do it itself:
php_date_set_tzdb(NULL) dereferences its argument.
### Who hits it
Any SAPI that restarts the engine in-process with timezonedb loaded:
- Apache mod_php on a graceful reload: php/pecl-datetime-timezonedb#16 has the backtrace
(seek_to_tz_position ← timelib_timezone_id_is_valid ←
OnUpdate_date_timezone ← zm_startup_date).
- FrankenPHP ≥ 1.12.5, which restarts the engine on a watched-file change, an OPcache restart or
opcache_reset():
php/frankenphp#2667.
### Reproduction (FrankenPHP 1.13.0, PHP 8.5.11 ZTS, Docker)
A minimal worker script with watch on one file; touch that file, then send
one request. With only timezonedb
loaded, the restart never completed in 9 of 10 fresh containers. Without it, 0 of 10. With
ZEND_DONT_UNLOAD_MODULES=1, which keeps the .so mapped, 0 of 20.
### Suggested fix
Reset the pointer before the INI entries are registered (or in MSHUTDOWN):
```c
PHP_MINIT_FUNCTION(date)
{
php_date_global_timezone_db = NULL;
php_date_global_timezone_db_enabled = 0;
REGISTER_INI_ENTRIES();
...
```
### PHP Version
PHP 8.5.11 (same code on master)
### Operating System
Debian (dunglas/frankenphp:1.13-php8.5), aarch64