[php-src] PR #24602: Fix leak of stream buckets left behind by misbehaving user filters
| From: | zeynallow | Date: | Fri, 23 Oct 2026 13:49:12 +0000 |
| Subject: | [php-src] PR #24602: Fix leak of stream buckets left behind by misbehaving user filters | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-39766@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/24602
Author: zeynallow
When a stream filter does not move the buckets of its input brigade (for example a
user filter that simply returns
PSFS_PASS_ON without calling
stream_bucket_make_writeable()), php_stream_write_filtered() and
php_stream_fill_read_buffer() swap the brigades and then memset() the old
input
brigade to zero. The unconsumed buckets are still linked to it, so they are leaked.
user_filters.c already warns about this situation
("Unprocessed filter buckets remaining on input brigade"), but nothing frees the buckets.
Separately, when a write filter returns PSFS_FEED_ME (or PSFS_ERR_FATAL)
after
appending buckets to the output brigade, php_stream_write_filtered() only cleans up
the input brigade, so the output buckets are leaked. The read path already discards
the output brigade in this case; the write path now does the same.
Reproducer (debug build):
```php
class f extends php_user_filter {
public function filter($in, $out, &$consumed, bool $closing): int {
return PSFS_PASS_ON;
}
}
stream_filter_register("f", "f");
$fp = fopen("php://memory", "w+");
stream_filter_append($fp, "f", STREAM_FILTER_WRITE);
fwrite($fp, "Hello");
// main/streams/filter.c(72) : Freeing ... (48 bytes)
// === Total 1 memory leaks detected ===
```
Changes:
- Add a small php_stream_bucket_brigade_discard() helper in
main/streams/streams.c.
- Discard leftover input buckets before swapping brigades, in both the read and write
filter chains (the data was already reported as unprocessed and was being dropped
anyway, so behaviour is unchanged apart from the leak).
- On PSFS_FEED_ME / PSFS_ERR_FATAL in the write path, also discard the
output brigade.
- New test ext/standard/tests/filters/user_filter_unconsumed_buckets_leak.phpt
covering the read/PASS_ON, write/PASS_ON and write/FEED_ME
cases.
- When merged up to PHP-8.6 / master, the --XFAIL-- This leaks memory sections of
stream_filter_register_mock_class_filter.phpt and
stream_filter_register_class_coerce_consumed_by_ref_param.phpt can be removed;
both pass with this fix.
Tested with a debug build: ext/standard/tests/filters and
ext/standard/tests/file
all pass on PHP-8.4; on master the two former XFAIL tests now pass.