diff --git a/NEWS b/NEWS index c10ef7b24c4de..aa539b4f40733 100644 --- a/NEWS +++ b/NEWS @@ -165,6 +165,7 @@ PHP NEWS is now a boolean. (David Carlier) . Fixed bug GH-16883 (gzopen() does not use the default stream context when opening HTTP URLs). (nielsdos) + . Implemented GH-17668 (zlib streams should support locking). (nielsdos) <<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> diff --git a/UPGRADING b/UPGRADING index eca064f3e4855..86b3dfe66ad92 100644 --- a/UPGRADING +++ b/UPGRADING @@ -107,6 +107,10 @@ PHP 8.5 UPGRADE NOTES and is not a QName because in those cases the namespace is taken from the namespace href or prefix respectively. +- Zlib: + . flock() is now supported on zlib streams. Previously, this always + failed to perform any locking action. + ======================================== 3. Changes in SAPI modules ======================================== diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 7cde9193c7bcf..5b051af9bb830 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -225,7 +225,7 @@ static const char *zend_asymmetric_visibility_string(uint32_t fn_flags) /* {{{ * } } -static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *name) { +static zend_string *resolve_class_name(const zend_class_entry *scope, zend_string *name) { ZEND_ASSERT(scope); if (zend_string_equals_literal_ci(name, "parent") && scope->parent) { if (scope->ce_flags & ZEND_ACC_RESOLVED_PARENT) { diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index aee2afa7a513d..0f2a149eadb06 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14733,7 +14733,11 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit, } if (may_throw) { - zend_jit_check_exception(jit); + if (Z_MODE(res_addr) == IS_REG) { + zend_jit_check_exception_undef_result(jit, opline); + } else { + zend_jit_check_exception(jit); + } } return 1; diff --git a/ext/opcache/tests/jit/gh17747.phpt b/ext/opcache/tests/jit/gh17747.phpt new file mode 100644 index 0000000000000..803d2201ec5b4 --- /dev/null +++ b/ext/opcache/tests/jit/gh17747.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-17747 (Exception on reading property in register-based FETCH_OBJ_R breaks JIT) +--EXTENSIONS-- +opcache +--INI-- +opcache.jit=function +--FILE-- +a); + } +} +$test = new C; +$test->test(); +?> +--EXPECTF-- +Fatal error: Uncaught Error: Typed property C::$a must not be accessed before initialization in %s:%d +Stack trace: +#0 %s(%d): C->test() +#1 {main} + thrown in %s on line %d diff --git a/ext/zlib/tests/005.txt.gz b/ext/zlib/tests/005.txt.gz new file mode 100644 index 0000000000000..07805db755807 Binary files /dev/null and b/ext/zlib/tests/005.txt.gz differ diff --git a/ext/zlib/tests/gh17745.phpt b/ext/zlib/tests/gh17745.phpt new file mode 100644 index 0000000000000..64331269dcdab --- /dev/null +++ b/ext/zlib/tests/gh17745.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-17745 (zlib extension incorrectly handles object arguments) +--EXTENSIONS-- +zlib +--FILE-- +level = 3; +var_dump(deflate_init(ZLIB_ENCODING_RAW, $obj)); + +class Options { + public int $level = 3; +} +var_dump(deflate_init(ZLIB_ENCODING_RAW, new Options)); +?> +--EXPECT-- +object(DeflateContext)#2 (0) { +} +object(DeflateContext)#3 (0) { +} diff --git a/ext/zlib/tests/zlib_lock_mandatory_windows.phpt b/ext/zlib/tests/zlib_lock_mandatory_windows.phpt new file mode 100644 index 0000000000000..04ed2ab251056 --- /dev/null +++ b/ext/zlib/tests/zlib_lock_mandatory_windows.phpt @@ -0,0 +1,52 @@ +--TEST-- +Test mandatory locking on Windows +--EXTENSIONS-- +zlib +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +without wrapper +bool(true) + +Notice: fread(): Read of 1 bytes failed with errno=13 Permission denied in %s on line %d +bool(false) +string(1) "W" + +with wrapper +bool(true) + +Notice: fread(): Read of 1 bytes failed with errno=13 Permission denied in %s on line %d +bool(false) +string(1) "W" diff --git a/ext/zlib/tests/zlib_wrapper_flock_basic.phpt b/ext/zlib/tests/zlib_wrapper_flock_basic.phpt index 95329dd003eb8..89656c7fca6bc 100644 --- a/ext/zlib/tests/zlib_wrapper_flock_basic.phpt +++ b/ext/zlib/tests/zlib_wrapper_flock_basic.phpt @@ -1,13 +1,13 @@ --TEST-- -Test function stream_get_meta_data on a zlib stream +Test function flock on a zlib stream --EXTENSIONS-- zlib --FILE-- --EXPECT-- -bool(false) +bool(true) diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 3a72bd506340f..1ad2cdf0e8cef 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -790,6 +790,7 @@ static bool zlib_create_dictionary_string(HashTable *options, char **dict, size_ zval *option_buffer; if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("dictionary"))) != NULL) { + ZVAL_DEINDIRECT(option_buffer); ZVAL_DEREF(option_buffer); switch (Z_TYPE_P(option_buffer)) { case IS_STRING: { @@ -871,6 +872,7 @@ PHP_FUNCTION(inflate_init) } if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("window"))) != NULL) { + ZVAL_DEINDIRECT(option_buffer); window = zval_get_long(option_buffer); } if (window < 8 || window > 15) { @@ -1089,6 +1091,7 @@ PHP_FUNCTION(deflate_init) } if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("level"))) != NULL) { + ZVAL_DEINDIRECT(option_buffer); level = zval_get_long(option_buffer); } if (level < -1 || level > 9) { @@ -1097,6 +1100,7 @@ PHP_FUNCTION(deflate_init) } if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("memory"))) != NULL) { + ZVAL_DEINDIRECT(option_buffer); memory = zval_get_long(option_buffer); } if (memory < 1 || memory > 9) { @@ -1105,6 +1109,7 @@ PHP_FUNCTION(deflate_init) } if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("window"))) != NULL) { + ZVAL_DEINDIRECT(option_buffer); window = zval_get_long(option_buffer); } if (window < 8 || window > 15) { @@ -1113,6 +1118,7 @@ PHP_FUNCTION(deflate_init) } if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("strategy"))) != NULL) { + ZVAL_DEINDIRECT(option_buffer); strategy = zval_get_long(option_buffer); } switch (strategy) { diff --git a/ext/zlib/zlib.stub.php b/ext/zlib/zlib.stub.php index 5708d9242d3a8..06f0f6d3ae8ea 100644 --- a/ext/zlib/zlib.stub.php +++ b/ext/zlib/zlib.stub.php @@ -270,11 +270,11 @@ function gzread($stream, int $length): string|false {} */ function gzgets($stream, ?int $length = null): string|false {} -function deflate_init(int $encoding, array $options = []): DeflateContext|false {} +function deflate_init(int $encoding, array|object $options = []): DeflateContext|false {} function deflate_add(DeflateContext $context, string $data, int $flush_mode = ZLIB_SYNC_FLUSH): string|false {} -function inflate_init(int $encoding, array $options = []): InflateContext|false {} +function inflate_init(int $encoding, array|object $options = []): InflateContext|false {} function inflate_add(InflateContext $context, string $data, int $flush_mode = ZLIB_SYNC_FLUSH): string|false {} diff --git a/ext/zlib/zlib_arginfo.h b/ext/zlib/zlib_arginfo.h index de5a538453861..81d779c1b7d17 100644 --- a/ext/zlib/zlib_arginfo.h +++ b/ext/zlib/zlib_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 65271ce06d23b397180a8dbbcecdb0cde5c6942b */ + * Stub hash: 4c5bea6d9f290c244c7bb27c77fe8007d43a40db */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ob_gzhandler, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) @@ -106,7 +106,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_deflate_init, 0, 1, DeflateContext, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, encoding, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_OBJECT, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_deflate_add, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) @@ -117,7 +117,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_inflate_init, 0, 1, InflateContext, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, encoding, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_OBJECT, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_inflate_add, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) diff --git a/ext/zlib/zlib_fopen_wrapper.c b/ext/zlib/zlib_fopen_wrapper.c index b414b33a8724e..4f07926e5e206 100644 --- a/ext/zlib/zlib_fopen_wrapper.c +++ b/ext/zlib/zlib_fopen_wrapper.c @@ -30,6 +30,18 @@ struct php_gz_stream_data_t { php_stream *stream; }; +static void php_gziop_report_errors(php_stream *stream, size_t count, const char *verb) +{ + if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { + struct php_gz_stream_data_t *self = stream->abstract; + int error = 0; + gzerror(self->gz_file, &error); + if (error == Z_ERRNO) { + php_error_docref(NULL, E_NOTICE, "%s of %zu bytes failed with errno=%d %s", verb, count, errno, strerror(errno)); + } + } +} + static ssize_t php_gziop_read(php_stream *stream, char *buf, size_t count) { struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *) stream->abstract; @@ -38,6 +50,11 @@ static ssize_t php_gziop_read(php_stream *stream, char *buf, size_t count) /* XXX this needs to be looped for the case count > UINT_MAX */ read = gzread(self->gz_file, buf, count); + /* Notify user of error, like the standard file wrapper normally does (e.g. errno=13 on mandatory lock failure). */ + if (UNEXPECTED(read < 0)) { + php_gziop_report_errors(stream, count, "Read"); + } + if (gzeof(self->gz_file)) { stream->eof = 1; } @@ -50,7 +67,14 @@ static ssize_t php_gziop_write(php_stream *stream, const char *buf, size_t count struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *) stream->abstract; /* XXX this needs to be looped for the case count > UINT_MAX */ - return gzwrite(self->gz_file, (char *) buf, count); + int written = gzwrite(self->gz_file, (char *) buf, count); + + /* Notify user of error, like the standard file wrapper normally does (e.g. errno=13 on mandatory lock failure). */ + if (UNEXPECTED(written < 0)) { + php_gziop_report_errors(stream, count, "Write"); + } + + return written; } static int php_gziop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) @@ -95,6 +119,21 @@ static int php_gziop_flush(php_stream *stream) return gzflush(self->gz_file, Z_SYNC_FLUSH); } +static int php_gziop_set_option(php_stream *stream, int option, int value, void *ptrparam) +{ + struct php_gz_stream_data_t *self = stream->abstract; + + switch (option) { + case PHP_STREAM_OPTION_LOCKING: + case PHP_STREAM_OPTION_META_DATA_API: + return self->stream->ops->set_option(self->stream, option, value, ptrparam); + default: + break; + } + + return PHP_STREAM_OPTION_RETURN_NOTIMPL; +} + const php_stream_ops php_stream_gzio_ops = { php_gziop_write, php_gziop_read, php_gziop_close, php_gziop_flush, @@ -102,7 +141,7 @@ const php_stream_ops php_stream_gzio_ops = { php_gziop_seek, NULL, /* cast */ NULL, /* stat */ - NULL /* set_option */ + php_gziop_set_option /* set_option */ }; php_stream *php_stream_gzopen(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,