Fix leaks from unprocessed user filter buckets - #23267
matthiasgoergens wants to merge 1 commit into
Conversation
|
Looks good to me. The same error message ("Unprocessed filter buckets remaining on input brigade") is present around line 190, if |
|
The drain is in the right function, but doing it unconditionally destroys data on the read path.
class defer extends php_user_filter {
private int $calls = 0;
public function filter($in, $out, &$consumed, bool $closing): int {
$this->calls++;
$buckets = [];
while ($b = stream_bucket_make_writeable($in)) { $buckets[] = $b; }
if ($this->calls < 3) {
foreach ($buckets as $b) { stream_bucket_prepend($in, $b); }
return PSFS_FEED_ME;
}
foreach ($buckets as $b) { stream_bucket_append($out, $b); }
return PSFS_PASS_ON;
}
}
stream_filter_register("defer", "defer");
$fp = fopen($threeChunkFile, 'r');
stream_filter_append($fp, "defer", STREAM_FILTER_READ);
var_dump(strlen(stream_get_contents($fp)));24576 on master, 8192 with this patch. The buckets are userland-owned after I do not think the deferral pattern is worth preserving as such, but silently truncating a stream is worse than the leak. Gating the drain on Two smaller things. The |
|
On the second warning site: the caller does not always clean up. On the branch: the leak is on 8.4 and 8.5 too, so the fix belongs on the lowest affected branch rather than master. The behavior question above is the reason to settle it on master first. Also worth linking up: #20058 does the same thing, has been open since October, and additionally drains |
|
@iliaal, closing this in favour of #20058, which fixes the same leak on PHP-8.3 and also removes the now-redundant caller-side drains in filter.c and streams.c. Your deferral case truncates on that branch too, measured on a debug build, so I have offered the |
…or any non-PASS_ON status _php_stream_fill_read_buffer() keeps the input brigade across calls, so a filter that returns PSFS_FEED_ME may put buckets back on $in to see them again with the next chunk. Draining the input brigade for every status frees those buckets and truncates the stream: a filter that defers twice on a three-chunk file returns 8192 bytes instead of 24576, as iliaal found while reviewing php#23267. Drain the input brigade only when the filter did not return PSFS_FEED_ME. Also drain leftover output buckets for any status other than PSFS_PASS_ON, so an out-of-range status cannot leave them linked to the stack brigade.
A user filter can return without having processed every bucket in its input
brigade; the stream layer then drops the brigade, orphaning the remaining
buckets. Unlink and release leftover buckets when the filter finishes. This
resolves two XFAILs in ext/standard/tests/filters.