From 20e75329f2adb11dd231852c061926d0e4080929 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 19 Aug 2020 18:19:08 +0200 Subject: 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] Closes GH-6019. --- NEWS | 3 +++ ext/zlib/tests/bug48725.phpt | 24 ++++++++++++++++++++++++ ext/zlib/zlib_filter.c | 14 ++++++++++---- 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 ext/zlib/tests/bug48725.phpt 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-- + +--FILE-- + +--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; -- cgit v1.2.1