From c8512b25b8b1f43ed99b0fec22f4ea79e994c188 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 17 Aug 2020 16:58:24 +0200 Subject: [PATCH 1/4] Fix #77069: stream filter loses final block of data Reading from a stream may return greater than zero, but nonetheless the stream's EOF flag may have been set. We have to cater to this condition by setting the close flag for filters. --- ext/standard/tests/streams/bug77069.phpt | 57 ++++++++++++++++++++++++ ext/standard/tests/streams/bug79984.phpt | 57 ++++++++++++++++++++++++ main/streams/streams.c | 2 +- 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/streams/bug77069.phpt create mode 100644 ext/standard/tests/streams/bug79984.phpt diff --git a/ext/standard/tests/streams/bug77069.phpt b/ext/standard/tests/streams/bug77069.phpt new file mode 100644 index 0000000000000..ec78ac25c52cd --- /dev/null +++ b/ext/standard/tests/streams/bug77069.phpt @@ -0,0 +1,57 @@ +--TEST-- +Bug #77069 (stream filter loses final block of data) +--FILE-- +data .= $bucket_in->data; + $consumed += $bucket_in->datalen; + + // Process whole lines. + while (preg_match('/(.*?)[\r\n]+(.*)/s', $this->data, $match) === 1) { + list(, $data, $this->data) = $match; + // Send this record output. + $data = strrev($data) . PHP_EOL; + $bucket_out = stream_bucket_new($this->stream, $data); + $return = PSFS_PASS_ON; + stream_bucket_append($out, $bucket_out); + } + } + + // Process the final line. + if ($closing && $this->data !== '') { + $data = strrev($this->data) . PHP_EOL; + $bucket_out = stream_bucket_new($this->stream, $data); + $return = PSFS_PASS_ON; + stream_bucket_append($out, $bucket_out); + } + + return $return; + } +} + +stream_filter_register('my-filter', 'MyFilter'); + +$input = "Line one\nLine two\nLine three"; + +$stream = fopen('data://text/plain,' . $input, 'r'); +stream_filter_append($stream, 'my-filter'); + +$output = ''; +while (!feof($stream)) { + $output .= fread($stream, 16); +} +fclose($stream); + +echo $output; +?> +--EXPECT-- +eno eniL +owt eniL +eerht eniL diff --git a/ext/standard/tests/streams/bug79984.phpt b/ext/standard/tests/streams/bug79984.phpt new file mode 100644 index 0000000000000..7126458ffff87 --- /dev/null +++ b/ext/standard/tests/streams/bug79984.phpt @@ -0,0 +1,57 @@ +--TEST-- +Bug #79984 (Stream filter is not called with closing arg) +--FILE-- +data = strtoupper($bucket->data); + $consumed += $bucket->datalen; + stream_bucket_append($out, $bucket); + } + echo 'filtered ' . ($consumed ? $consumed : 0) . ' bytes'; + if ($closing) { + echo ' and closing.'; + } else { + echo '.'; + } + if (feof($this->stream)) { + echo ' Stream has reached end-of-file.'; + } + echo PHP_EOL; + return PSFS_PASS_ON; + } +} + +stream_filter_register('f', 'F'); + +$str = str_repeat('a', 8320); + +$f2 = fopen('php://temp', 'r+b'); +fwrite($f2, $str); +fseek($f2, 0, SEEK_SET); +stream_filter_append($f2, 'f', STREAM_FILTER_READ); +var_dump(strlen(stream_get_contents($f2))); +fclose($f2); + +?> +--EXPECT-- +filter onCreate +filtered 8192 bytes. +filtered 128 bytes and closing. +int(8320) +filter onClose diff --git a/main/streams/streams.c b/main/streams/streams.c index cf411a1dd3751..ab413872e0bd4 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -567,7 +567,7 @@ PHPAPI int _php_stream_fill_read_buffer(php_stream *stream, size_t size) /* after this call, bucket is owned by the brigade */ php_stream_bucket_append(brig_inp, bucket); - flags = PSFS_FLAG_NORMAL; + flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_NORMAL; } else { flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC; } From 43a4a83c218b3cb7f5324c38093f2d28552ec4be Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 19 Aug 2020 12:41:57 +0200 Subject: [PATCH 2/4] Add a test case for zlib.deflate Cf. . --- ext/zlib/tests/bug48725_2.phpt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 ext/zlib/tests/bug48725_2.phpt diff --git a/ext/zlib/tests/bug48725_2.phpt b/ext/zlib/tests/bug48725_2.phpt new file mode 100644 index 0000000000000..168afd8d3dc54 --- /dev/null +++ b/ext/zlib/tests/bug48725_2.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #48725 (Support for flushing in zlib stream) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +72cbcf57484a2c02e22a00000000ffff0300 From 14145d57ecabf147085c2d7aedd754f3780678db Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Thu, 27 Aug 2020 12:35:28 +0200 Subject: [PATCH 3/4] Fix zlib.inflate filter If `inflate()` is called with flush mode `Z_FINISH`, but the output buffer is not large enough to inflate all available data, it fails with `Z_BUF_ERROR`. However, `Z_BUF_ERROR` is not fatal; in fact, the zlib manual states: "If deflate returns with Z_OK or Z_BUF_ERROR, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error." Hence, we do so. This fixes the so far failing ext/soap/tests/bug73037.phpt. --- ext/zlib/zlib_filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c index 3654a140fd5a6..3c19d380169a1 100644 --- a/ext/zlib/zlib_filter.c +++ b/ext/zlib/zlib_filter.c @@ -90,7 +90,7 @@ static php_stream_filter_status_t php_zlib_inflate_filter( inflateEnd(&(data->strm)); data->finished = '\1'; exit_status = PSFS_PASS_ON; - } else if (status != Z_OK) { + } else if (status != Z_OK && status != Z_BUF_ERROR) { /* Something bad happened */ php_stream_bucket_delref(bucket); /* reset these because despite the error the filter may be used again */ From 0e4b9ff73aee0942b170598dd49a4e441f3d67ca Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 22 Sep 2020 16:02:01 +0200 Subject: [PATCH 4/4] Add test case for bug #77080 --- ext/standard/tests/streams/bug77080.phpt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 ext/standard/tests/streams/bug77080.phpt diff --git a/ext/standard/tests/streams/bug77080.phpt b/ext/standard/tests/streams/bug77080.phpt new file mode 100644 index 0000000000000..feb20656d63c8 --- /dev/null +++ b/ext/standard/tests/streams/bug77080.phpt @@ -0,0 +1,16 @@ +--TEST-- +Bug #77080 (Deflate not working) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true)