summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph M. Becker <cmbecker69@gmx.de>2020-08-19 18:19:08 +0200
committerChristoph M. Becker <cmbecker69@gmx.de>2020-12-08 12:23:24 +0100
commit20e75329f2adb11dd231852c061926d0e4080929 (patch)
treee4a41a2393d16ba7f74ecd5c8810a09a59580f65
parent65f5573bc82108bbaf2727ffa11575f3292d736f (diff)
downloadphp-git-20e75329f2adb11dd231852c061926d0e4080929.tar.gz
Fix #48725: Support for flushing in zlib stream
When `php_zlib_deflate_filter()` is called with `PSFS_FLAG_FLUSH_INC` but without new buckets being available (e.g. because a user calls `rewind()` after writing to the stream), we have to make sure that any pending data are flushed. This could basically be done like in the attached patch[1], but that could cause unnessary flushes, which can be harmful for compression, and adds unnecessary flush markers to the stream. Thus, we use the `php_zlib_filter_data.finished` field, which has not been used for `zlib.deflate` filters, and properly keep track of the need to flush. [1] <https://bugs.php.net/patch-display.php?bug_id=48725&patch=zlib-filter-flush-fix.patch&revision=latest> Closes GH-6019.
-rw-r--r--NEWS3
-rw-r--r--ext/zlib/tests/bug48725.phpt24
-rw-r--r--ext/zlib/zlib_filter.c14
3 files changed, 37 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 0de562eeaf..9560df1c93 100644
--- a/NEWS
+++ b/NEWS
@@ -46,6 +46,9 @@ PHP NEWS
- Tidy:
. Fixed bug #77594 (ob_tidyhandler is never reset). (cmb)
+- Zlib:
+ . Fixed #48725 (Support for flushing in zlib stream). (cmb)
+
26 Nov 2020, PHP 7.4.13
- Core:
diff --git a/ext/zlib/tests/bug48725.phpt b/ext/zlib/tests/bug48725.phpt
new file mode 100644
index 0000000000..0d04887a2d
--- /dev/null
+++ b/ext/zlib/tests/bug48725.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #48725 (Support for flushing in zlib stream)
+--SKIPIF--
+<?php
+if (!extension_loaded('zlib')) die('skip zip extension not available');
+?>
+--FILE--
+<?php
+$text = str_repeat('0123456789abcdef', 1000);
+
+$temp = fopen('php://temp', 'r+');
+stream_filter_append($temp, 'zlib.deflate', STREAM_FILTER_WRITE);
+fwrite($temp, $text);
+
+rewind($temp);
+
+var_dump(bin2hex(stream_get_contents($temp)));
+var_dump(ftell($temp));
+
+fclose($temp);
+?>
+--EXPECT--
+string(138) "ecc7c901c0100000b09594bac641d97f840e22f9253c31bdb9d4d6c75cdf3ec1ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddaffc0f0000ffff"
+int(69)
diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c
index 3c19d38016..988ed603ca 100644
--- a/ext/zlib/zlib_filter.c
+++ b/ext/zlib/zlib_filter.c
@@ -29,7 +29,7 @@ typedef struct _php_zlib_filter_data {
unsigned char *outbuf;
size_t outbuf_len;
int persistent;
- zend_bool finished;
+ zend_bool finished; /* for zlib.deflate: signals that no flush is pending */
} php_zlib_filter_data;
/* }}} */
@@ -196,6 +196,8 @@ static php_stream_filter_status_t php_zlib_deflate_filter(
bucket = php_stream_bucket_make_writeable(bucket);
while (bin < (unsigned int) bucket->buflen) {
+ int flush_mode;
+
desired = bucket->buflen - bin;
if (desired > data->inbuf_len) {
desired = data->inbuf_len;
@@ -203,7 +205,9 @@ static php_stream_filter_status_t php_zlib_deflate_filter(
memcpy(data->strm.next_in, bucket->buf + bin, desired);
data->strm.avail_in = desired;
- status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH));
+ flush_mode = flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH);
+ data->finished = flush_mode != Z_NO_FLUSH;
+ status = deflate(&(data->strm), flush_mode);
if (status != Z_OK) {
/* Something bad happened */
php_stream_bucket_delref(bucket);
@@ -230,11 +234,12 @@ static php_stream_filter_status_t php_zlib_deflate_filter(
php_stream_bucket_delref(bucket);
}
- if (flags & PSFS_FLAG_FLUSH_CLOSE) {
+ if (flags & PSFS_FLAG_FLUSH_CLOSE || ((flags & PSFS_FLAG_FLUSH_INC) && !data->finished)) {
/* Spit it out! */
status = Z_OK;
while (status == Z_OK) {
- status = deflate(&(data->strm), Z_FINISH);
+ status = deflate(&(data->strm), (flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH));
+ data->finished = 1;
if (data->strm.avail_out < data->outbuf_len) {
size_t bucketlen = data->outbuf_len - data->strm.avail_out;
@@ -395,6 +400,7 @@ factory_setlevel:
}
}
status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0);
+ data->finished = 1;
fops = &php_zlib_deflate_ops;
} else {
status = Z_DATA_ERROR;