[php-src] Issue #24579: Crash in FILTER_VALIDATE_INT when __toString() mutates the referenced options array
| From: | Meecoy | Date: | Thu, 22 Oct 2026 20:42:58 +0000 |
| Subject: | [php-src] Issue #24579: Crash in FILTER_VALIDATE_INT when __toString() mutates the referenced options array | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253116@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24579
Author: Meecoy
### Description
# Crash in
FILTER_VALIDATE_INT when __toString() mutates the referenced
options array
## Summary
Calling filter_var() with FILTER_VALIDATE_INT crashes when the input
object implements __toString() and modifies the options array referenced by the filter
arguments during string conversion.
Reproduced on PHP 8.5.12.
A non-debug build terminated with SIGSEGV. A debug build terminated with
SIGABRT after Zend detected an inconsistent hash table state.
## Minimal reproducer
```php
<?php
$shared = ['min_range' => 0];
$args = ['options' => &$shared];
class Reenter
{
public function __toString(): string
{
global $shared;
$shared = 'changed';
return '1';
}
}
filter_var(new Reenter(), FILTER_VALIDATE_INT, $args);
```
## Steps to reproduce
1. Build PHP 8.5.12 from php-src with the Filter extension enabled.
2. Run the script above using the built PHP binary.
Build configuration used on Arch Linux:
```bash
./buildconf --force
./configure --disable-all --enable-cli --enable-debug --enable-filter \
CFLAGS="-O0 -g3 -fno-omit-frame-pointer"
make -j"$(nproc)"
```
Run the reproducer:
```bash
./sapi/cli/php repro.php
```
## Observed behavior
In the debug build, execution terminates with an assertion failure similar to:
```text
Zend/zend_hash.c(2919) : ht=0x7ffff78027c0 is being cleaned
php: Zend/zend_hash.c:73: _zend_is_inconsistent:
Assertion `0' failed.
Program received signal SIGABRT
```
The backtrace points to zend_hash_str_find() called from php_filter_int():
```text
#5 _zend_is_inconsistent(...)
#6 zend_hash_str_find(ht=0x7ffff78027c0, str="min_range", len=9)
#7 php_filter_int(...)
at ext/filter/logical_filters.c:213
#8 php_zval_filter(...)
at ext/filter/filter.c:289
#9 php_filter_call(...)
at ext/filter/filter.c:610
#10 zif_filter_var(...)
at ext/filter/filter.c:787
```
At frame 7, the option_array zval was observed to have type IS_STRING,
while the filter code attempted to access it as a hash table through
Z_ARRVAL_P(option_array).
A non-debug build previously terminated with SIGSEGV for the same reproducer.
## Expected behavior
The call should not crash the PHP process.
If the options argument becomes invalid during reentrant execution, PHP should handle the situation
safely, for example by rejecting invalid options or returning a filter failure according to the
applicable API semantics.
## Initial investigation
In ext/filter/logical_filters.c, the FETCH_LONG_OPTION macro accesses the
options argument as a hash table:
```c
#define FETCH_LONG_OPTION(var_name, option_name) \
var_name = 0; \
var_name##_set = 0; \
if (option_array) { \
if ((option_val = zend_hash_str_find( \
Z_ARRVAL_P(option_array), \
option_name, \
sizeof(option_name) - 1)) != NULL) { \
var_name = zval_get_long(option_val); \
var_name##_set = 1; \
} \
}
```
The macro checks whether option_array is non-null, but does not explicitly check that
the zval is an array before passing its contents to zend_hash_str_find().
The filter call path performs string conversion on the input value before invoking the filter
function. Since __toString() can execute userland code and mutate $shared,
the options value may change during this conversion.
This appears to create a reentrancy-related invalid-state condition in the filter options handling.
The precise underlying mechanism has not yet been confirmed.
## Environment
- PHP version: 8.5.12
- Operating system: Arch Linux
- Build: locally compiled from php-src, with debug symbols
- Configure options: --disable-all --enable-cli --enable-debug --enable-filter
- Compiler optimization: -O0
## Suggested investigation
- Verify whether the options zval can be changed through the referenced argument during input string
conversion.
- Check whether the filter implementation should validate the zval type before using
Z_ARRVAL_P().
- Review the relevant argument handling and ownership semantics in php_filter_call(),
php_zval_filter(), and zif_filter_var().
- Add a regression test ensuring that reentrant mutation of the options argument cannot crash PHP.
### PHP Version
```plain
PHP 8.5.12 (cli) (built: Oct 21 2026 20:05:19) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.5.12, Copyright (c) Zend Technologies
with Zend OPcache v8.5.12, Copyright (c), by Zend Technologies
```
### Operating System
Linux 7.2.8-2-cachyos