From aa3e9e8362eacdacd0e1aff5d179380e7799a8a9 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 22 Aug 2026 14:46:28 +0100 Subject: [PATCH] ext/zlib: deflate_init() assertion failure on uninitialised typed properties. Fix #22142 An object passed as options exposes uninitialised typed properties as IS_UNDEF slots, which zval_try_get_long() rejected through an unreachable branch. Those slots are now skipped, matching get_object_vars(). --- ext/zlib/tests/gh22142.phpt | 50 +++++++++++++++++++++++++++++ ext/zlib/tests/gh22142_inflate.phpt | 21 ------------ ext/zlib/zlib.c | 23 ++++++++++--- 3 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 ext/zlib/tests/gh22142.phpt delete mode 100644 ext/zlib/tests/gh22142_inflate.phpt diff --git a/ext/zlib/tests/gh22142.phpt b/ext/zlib/tests/gh22142.phpt new file mode 100644 index 000000000000..d41fab7e3312 --- /dev/null +++ b/ext/zlib/tests/gh22142.phpt @@ -0,0 +1,50 @@ +--TEST-- +GH-22142 (Assertion failure in zendi_try_get_long() on IS_UNDEF) +--CREDITS-- +JIANG Yuancheng +--EXTENSIONS-- +zlib +--INI-- +error_reporting=E_ALL & ~E_DEPRECATED +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +ValueError: deflate_init(): "level" option must be between -1 and 9 diff --git a/ext/zlib/tests/gh22142_inflate.phpt b/ext/zlib/tests/gh22142_inflate.phpt deleted file mode 100644 index 853099e4911d..000000000000 --- a/ext/zlib/tests/gh22142_inflate.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -GH-22142 (Assertion failure in zendi_try_get_long() on IS_UNDEF) ---EXTENSIONS-- -zlib ---FILE-- -getMessage(), PHP_EOL; -} - -?> ---EXPECTF-- -Deprecated: inflate_init(): Passing an object for argument #2 $option to inflate_init() is deprecated, call get_object_vars() first instead in %s on line %d -TypeError: inflate_init(): Argument #2 ($options) the value for option "window" must be of type int, null given diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 44ab23233ac8..f88350081fe3 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -779,11 +779,26 @@ PHP_ZLIB_DECODE_FUNC(gzdecode, PHP_ZLIB_ENCODING_GZIP); PHP_ZLIB_DECODE_FUNC(gzuncompress, PHP_ZLIB_ENCODING_DEFLATE); /* }}} */ +ZEND_ATTRIBUTE_NONNULL static zval *zlib_find_option(HashTable *options, const char *name, size_t name_len) +{ + zval *option = zend_hash_str_find(options, name, name_len); + + if (!option) { + return NULL; + } + + ZVAL_DEINDIRECT(option); + + if (UNEXPECTED(Z_TYPE_P(option) == IS_UNDEF)) { + return NULL; + } + return option; +} + static bool zlib_create_dictionary_string(HashTable *options, char **dict, size_t *dictlen) { zval *option_buffer; - if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("dictionary"))) != NULL) { - ZVAL_DEINDIRECT(option_buffer); + if (options && (option_buffer = zlib_find_option(options, ZEND_STRL("dictionary"))) != NULL) { ZVAL_DEREF(option_buffer); switch (Z_TYPE_P(option_buffer)) { case IS_STRING: { @@ -853,14 +868,12 @@ static bool zlib_create_dictionary_string(HashTable *options, char **dict, size_ ZEND_ATTRIBUTE_NONNULL static bool zlib_get_long_option(HashTable *options, const char *option_name, size_t option_name_len, zend_long *value) { bool failed = false; - zval *option_buffer = zend_hash_str_find(options, option_name, option_name_len); + zval *option_buffer = zlib_find_option(options, option_name, option_name_len); if (!option_buffer) { return true; } - /* The |H ZPP specifier may leave HashTable entries wrapped in IS_INDIRECT. */ - ZVAL_DEINDIRECT(option_buffer); *value = zval_try_get_long(option_buffer, &failed); if (UNEXPECTED(failed)) { zend_argument_type_error(