[php-src] PR #24163: Fix heap overflow when a read filter changes the stream's chunk size
| From: | EdmondDantes | Date: | Tue, 06 Oct 2026 14:42:11 +0000 |
| Subject: | [php-src] PR #24163: Fix heap overflow when a read filter changes the stream's chunk size | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-39211@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/24163
Author: EdmondDantes
_php_stream_fill_read_buffer() allocates chunk_buf from
stream->chunk_size once, but each turn of the filtered read loop reads with the
current stream->chunk_size:
```c
chunk_buf = emalloc(stream->chunk_size);
...
justread = stream->ops->read(stream, chunk_buf, stream->chunk_size);
```
A read filter that calls stream_set_chunk_size() on its stream (or code running while
the filter's Fiber is suspended) makes the next read write past chunk_buf. A debug
build reports zend_mm_heap corrupted.
The fix takes the chunk size once for the whole loop.
Test: ext/standard/tests/streams/stream_set_chunk_size_in_read_filter.phpt, a filter
that calls stream_set_chunk_size($this->stream, 4000000) and keeps asking for input.
It segfaults without the fix and passes with it.
Suggested NEWS entry (left out of the commit to avoid conflicts):
```
- Streams:
. Fixed heap overflow when a read filter changes the stream's chunk size.
(Edmond Dantes)
```
P.S.
This PR fixes the heap overflow in a bugfix-safe way: the filtered read loop takes the chunk size
once, so a change made by the filter applies from the next read.
I would also like to propose, for master, refusing the call itself. Changing the chunk size of a
stream while its own read filter is running is almost certainly a mistake in user code, and silently
deferring it hides that mistake.
There is a precedent: fclose() is already refused while a user filter runs.
userfilter_filter() sets PHP_STREAM_FLAG_NO_FCLOSE on the stream for the
duration of the callback, and fclose() then emits a warning and returns
false. stream_set_chunk_size() could follow the same pattern: a flag set
around the user filter callback, and a ValueError (or a warning plus
false, to match fclose()) when the chunk size is changed while it is set.