mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix OSS-Fuzz #385993744
PSFS_FEED_ME is supposed to be returned when the filter did not receive enough data and did not generate buckets for the output brigade. The test generates buckets anyway on the output brigade, and the stream layer did not handle that case causing a memory leak. To solve this, discard any such buckets as it would conflict with the status code returned by the filter. This keeps BC and solves the leak. Closes GH-18972.
This commit is contained in:
1
NEWS
1
NEWS
@@ -59,6 +59,7 @@ PHP NEWS
|
||||
- Streams:
|
||||
. Remove incorrect call to zval_ptr_dtor() in user_wrapper_metadata().
|
||||
(nielsdos)
|
||||
. Fix OSS-Fuzz #385993744. (nielsdos)
|
||||
|
||||
- Tidy:
|
||||
. Fixed GH-19021 build issue with libtidy in regard of tidyOptIsReadonly
|
||||
|
||||
28
ext/standard/tests/filters/oss_fuzz_385993744.phpt
Normal file
28
ext/standard/tests/filters/oss_fuzz_385993744.phpt
Normal file
@@ -0,0 +1,28 @@
|
||||
--TEST--
|
||||
OSS-Fuzz #385993744
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class SampleFilter extends php_user_filter
|
||||
{
|
||||
private $data = '';
|
||||
|
||||
public function filter($in, $out, &$consumed, $closing): int
|
||||
{
|
||||
while ($bucket = stream_bucket_make_writeable($in))
|
||||
{
|
||||
$this->data .= $bucket->data;
|
||||
}
|
||||
|
||||
$bucket = stream_bucket_new($this->stream, $this->data);
|
||||
stream_bucket_append($out, $bucket);
|
||||
|
||||
return PSFS_FEED_ME;
|
||||
}
|
||||
}
|
||||
stream_filter_register('sample.filter', SampleFilter::class);
|
||||
var_dump(file_get_contents('php://filter/read=sample.filter/resource='. __FILE__));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(0) ""
|
||||
@@ -354,6 +354,13 @@ PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_strea
|
||||
Reset stream's internal read buffer since the filter is "holding" it. */
|
||||
stream->readpos = 0;
|
||||
stream->writepos = 0;
|
||||
|
||||
/* Filter could have added buckets anyway, but signalled that it did not return any. Discard them. */
|
||||
while (brig_out.head) {
|
||||
bucket = brig_out.head;
|
||||
php_stream_bucket_unlink(bucket);
|
||||
php_stream_bucket_delref(bucket);
|
||||
}
|
||||
break;
|
||||
case PSFS_PASS_ON:
|
||||
/* If any data is consumed, we cannot rely upon the existing read buffer,
|
||||
|
||||
@@ -630,6 +630,12 @@ PHPAPI zend_result _php_stream_fill_read_buffer(php_stream *stream, size_t size)
|
||||
/* when a filter needs feeding, there is no brig_out to deal with.
|
||||
* we simply continue the loop; if the caller needs more data,
|
||||
* we will read again, otherwise out job is done here */
|
||||
|
||||
/* Filter could have added buckets anyway, but signalled that it did not return any. Discard them. */
|
||||
while ((bucket = brig_outp->head)) {
|
||||
php_stream_bucket_unlink(bucket);
|
||||
php_stream_bucket_delref(bucket);
|
||||
}
|
||||
break;
|
||||
|
||||
case PSFS_ERR_FATAL:
|
||||
@@ -1263,14 +1269,22 @@ static ssize_t _php_stream_write_filtered(php_stream *stream, const char *buf, s
|
||||
php_stream_bucket_delref(bucket);
|
||||
}
|
||||
break;
|
||||
case PSFS_FEED_ME:
|
||||
/* need more data before we can push data through to the stream */
|
||||
break;
|
||||
|
||||
case PSFS_ERR_FATAL:
|
||||
/* some fatal error. Theoretically, the stream is borked, so all
|
||||
* further writes should fail. */
|
||||
return (ssize_t) -1;
|
||||
consumed = (ssize_t) -1;
|
||||
ZEND_FALLTHROUGH;
|
||||
|
||||
case PSFS_FEED_ME:
|
||||
/* need more data before we can push data through to the stream */
|
||||
/* Filter could have added buckets anyway, but signalled that it did not return any. Discard them. */
|
||||
while (brig_inp->head) {
|
||||
bucket = brig_inp->head;
|
||||
php_stream_bucket_unlink(bucket);
|
||||
php_stream_bucket_delref(bucket);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
||||
Reference in New Issue
Block a user