Skip to content

Commit 20e7532

Browse files
committed
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.
1 parent 65f5573 commit 20e7532

File tree

3 files changed

+37
-4
lines changed

3 files changed

+37
-4
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ PHP NEWS
4646
- Tidy:
4747
. Fixed bug #77594 (ob_tidyhandler is never reset). (cmb)
4848

49+
- Zlib:
50+
. Fixed #48725 (Support for flushing in zlib stream). (cmb)
51+
4952
26 Nov 2020, PHP 7.4.13
5053

5154
- Core:

ext/zlib/tests/bug48725.phpt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Bug #48725 (Support for flushing in zlib stream)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('zlib')) die('skip zip extension not available');
6+
?>
7+
--FILE--
8+
<?php
9+
$text = str_repeat('0123456789abcdef', 1000);
10+
11+
$temp = fopen('php://temp', 'r+');
12+
stream_filter_append($temp, 'zlib.deflate', STREAM_FILTER_WRITE);
13+
fwrite($temp, $text);
14+
15+
rewind($temp);
16+
17+
var_dump(bin2hex(stream_get_contents($temp)));
18+
var_dump(ftell($temp));
19+
20+
fclose($temp);
21+
?>
22+
--EXPECT--
23+
string(138) "ecc7c901c0100000b09594bac641d97f840e22f9253c31bdb9d4d6c75cdf3ec1ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddaffc0f0000ffff"
24+
int(69)

ext/zlib/zlib_filter.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ typedef struct _php_zlib_filter_data {
2929
unsigned char *outbuf;
3030
size_t outbuf_len;
3131
int persistent;
32-
zend_bool finished;
32+
zend_bool finished; /* for zlib.deflate: signals that no flush is pending */
3333
} php_zlib_filter_data;
3434

3535
/* }}} */
@@ -196,14 +196,18 @@ static php_stream_filter_status_t php_zlib_deflate_filter(
196196
bucket = php_stream_bucket_make_writeable(bucket);
197197

198198
while (bin < (unsigned int) bucket->buflen) {
199+
int flush_mode;
200+
199201
desired = bucket->buflen - bin;
200202
if (desired > data->inbuf_len) {
201203
desired = data->inbuf_len;
202204
}
203205
memcpy(data->strm.next_in, bucket->buf + bin, desired);
204206
data->strm.avail_in = desired;
205207

206-
status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH));
208+
flush_mode = flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH);
209+
data->finished = flush_mode != Z_NO_FLUSH;
210+
status = deflate(&(data->strm), flush_mode);
207211
if (status != Z_OK) {
208212
/* Something bad happened */
209213
php_stream_bucket_delref(bucket);
@@ -230,11 +234,12 @@ static php_stream_filter_status_t php_zlib_deflate_filter(
230234
php_stream_bucket_delref(bucket);
231235
}
232236

233-
if (flags & PSFS_FLAG_FLUSH_CLOSE) {
237+
if (flags & PSFS_FLAG_FLUSH_CLOSE || ((flags & PSFS_FLAG_FLUSH_INC) && !data->finished)) {
234238
/* Spit it out! */
235239
status = Z_OK;
236240
while (status == Z_OK) {
237-
status = deflate(&(data->strm), Z_FINISH);
241+
status = deflate(&(data->strm), (flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH));
242+
data->finished = 1;
238243
if (data->strm.avail_out < data->outbuf_len) {
239244
size_t bucketlen = data->outbuf_len - data->strm.avail_out;
240245

@@ -395,6 +400,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f
395400
}
396401
}
397402
status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0);
403+
data->finished = 1;
398404
fops = &php_zlib_deflate_ops;
399405
} else {
400406
status = Z_DATA_ERROR;

0 commit comments

Comments
 (0)