diff --git a/NEWS b/NEWS index 5437086c8e7dd..405d6fe5f47c0 100644 --- a/NEWS +++ b/NEWS @@ -320,6 +320,10 @@ PHP NEWS . Fixed bug GH-20439 (xml_set_default_handler() does not properly handle special characters in attributes when passing data to callback). (ndossche) +- Zlib: + . Fixed bug GH-21376 (gzfile and readgzfile no longer detect corrupted gzip + data). (GabrielCordeiroBarrosoTeles) + - Zip: . Fix crash in property existence test. (ndossche) . Don't truncate return value of zip_fread() with user sizes. (ndossche) diff --git a/ext/zlib/tests/bug21376_corrupt_gz.phpt b/ext/zlib/tests/bug21376_corrupt_gz.phpt new file mode 100644 index 0000000000000..ffe779e9bd129 --- /dev/null +++ b/ext/zlib/tests/bug21376_corrupt_gz.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #21376: gzfile and readgzfile must detect corrupted gzip data +--EXTENSIONS-- +zlib +--FILE-- + +--EXPECT-- +array(0) { +} +int(-1) diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index e73e168708ad7..6cac86183d40a 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -27,6 +27,7 @@ #include "php_ini.h" #include "ext/standard/info.h" #include "ext/standard/file.h" +#include "Zend/zend_virtual_cwd.h" #include "php_zlib.h" #include "zlib_arginfo.h" @@ -677,7 +678,7 @@ PHP_FUNCTION(readgzfile) size_t filename_len; int flags = REPORT_ERRORS; php_stream *stream; - size_t size; + ssize_t size; bool use_include_path = false; if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|b", &filename, &filename_len, &use_include_path) == FAILURE) { @@ -694,8 +695,20 @@ PHP_FUNCTION(readgzfile) RETURN_FALSE; } size = php_stream_passthru(stream); + /* On Windows, corrupt gzip may yield 0 from passthru; stream eof is already set so a further read returns 0 (not -1). Treat 0 bytes read from a non-empty file as error. */ + if (size == 0) { + char buf[1]; + if (php_stream_read(stream, buf, 1) < 0) { + size = -1; + } else { + zend_stat_t st = {0}; + if (VCWD_STAT(filename, &st) == 0 && st.st_size > 0) { + size = -1; + } + } + } php_stream_close(stream); - RETURN_LONG(size); + RETURN_LONG((zend_long) size); } /* }}} */ diff --git a/ext/zlib/zlib_fopen_wrapper.c b/ext/zlib/zlib_fopen_wrapper.c index 7420c0cc6ff2e..a0b4f3d17ab1d 100644 --- a/ext/zlib/zlib_fopen_wrapper.c +++ b/ext/zlib/zlib_fopen_wrapper.c @@ -65,6 +65,17 @@ static ssize_t php_gziop_read(php_stream *stream, char *buf, size_t count) return read; } + /* Corrupt gzip data: gzread() may return 0 or bytes while gzerror indicates Z_DATA_ERROR (e.g. on Windows) */ + if (read >= 0) { + int zerr; + gzerror(self->gz_file, &zerr); + if (zerr == Z_DATA_ERROR || zerr == Z_STREAM_ERROR) { + stream->eof = 1; + php_gziop_report_errors(stream, chunk_size, "Read"); + return -1; + } + } + total_read += read; buf += read; } while (count > 0 && !stream->eof);