diff --git a/NEWS b/NEWS index c7645ca27b8a..858ca396189a 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ PHP NEWS invalid variable names). (timwolla) . Fixed bug GH-22291 (AST pretty printing does not correctly handle braces in string interpolation). (timwolla) + . Fixed bug GH-22373 (AST pretty-printing drops meaningful parentheses + surrounding property access). (timwolla) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index f495c4c8e3bb..57faedc06f9b 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2535,12 +2535,18 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio break; case ZEND_AST_CALL: { zend_ast *left = ast->child[0]; - if (left->kind == ZEND_AST_ARROW_FUNC || left->kind == ZEND_AST_CLOSURE) { - smart_str_appendc(str, '('); - zend_ast_export_ns_name(str, left, 0, indent); - smart_str_appendc(str, ')'); - } else { - zend_ast_export_ns_name(str, left, 0, indent); + switch (left->kind) { + /* ZEND_AST_ZVAL is a regular function call. */ + case ZEND_AST_ZVAL: + /* ZEND_AST_VAR ($foo()) is unambiguous without parens. */ + case ZEND_AST_VAR: + zend_ast_export_ns_name(str, left, 0, indent); + break; + default: + smart_str_appendc(str, '('); + zend_ast_export_ex(str, left, 0, indent); + smart_str_appendc(str, ')'); + break; } smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); diff --git a/ext/com_dotnet/com_com.c b/ext/com_dotnet/com_com.c index d9938a13c950..a0c8a955e0c5 100644 --- a/ext/com_dotnet/com_com.c +++ b/ext/com_dotnet/com_com.c @@ -322,7 +322,7 @@ PHP_FUNCTION(com_get_active_object) IDispatch_Release(obj); } if (unk) { - IUnknown_Release(obj); + IUnknown_Release(unk); } efree(module); } diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 19f83e3140be..70a379dbe9be 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -533,7 +533,7 @@ PHP_FUNCTION(imageloadfont) */ font = (gdFontPtr) emalloc(sizeof(gdFont)); b = 0; - while (b < hdr_size && (n = php_stream_read(stream, (char*)&font[b], hdr_size - b)) > 0) { + while (b < hdr_size && (n = php_stream_read(stream, (char *) font + b, hdr_size - b)) > 0) { b += n; } diff --git a/ext/gd/tests/imageloadfont_short_read.phpt b/ext/gd/tests/imageloadfont_short_read.phpt new file mode 100644 index 000000000000..5a7f6a14c9bb --- /dev/null +++ b/ext/gd/tests/imageloadfont_short_read.phpt @@ -0,0 +1,65 @@ +--TEST-- +imageloadfont(): header read must stay in bounds on short reads +--EXTENSIONS-- +gd +--FILE-- +data = pack('V4', 1, 32, 1, 1) . "\x00"; + return true; + } + + public function stream_read($count): string + { + return $this->pos < strlen($this->data) ? $this->data[$this->pos++] : ''; + } + + public function stream_eof(): bool + { + return $this->pos >= strlen($this->data); + } + + public function stream_stat() + { + return []; + } + + public function stream_tell(): int + { + return $this->pos; + } + + public function stream_seek($offset, $whence): bool + { + if ($whence === SEEK_CUR) { + $this->pos += $offset; + } elseif ($whence === SEEK_END) { + $this->pos = strlen($this->data) + $offset; + } else { + $this->pos = $offset; + } + return true; + } + + public function stream_set_option($option, $arg1, $arg2): bool + { + return false; + } +} + +stream_wrapper_register('drip', drip::class); +var_dump(imageloadfont('drip://font') instanceof GdFont); +?> +--EXPECT-- +bool(true) diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c index b6c3ae03f689..bd246abb53a5 100644 --- a/ext/sodium/libsodium.c +++ b/ext/sodium/libsodium.c @@ -1469,6 +1469,7 @@ PHP_FUNCTION(sodium_crypto_pwhash) } if (memlimit < crypto_pwhash_MEMLIMIT_MIN) { zend_argument_error(sodium_exception_ce, 5, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + RETURN_THROWS(); } hash = zend_string_alloc((size_t) hash_len, 0); ret = -1; @@ -1528,9 +1529,11 @@ PHP_FUNCTION(sodium_crypto_pwhash_str) } if (opslimit < crypto_pwhash_OPSLIMIT_MIN) { zend_argument_error(sodium_exception_ce, 2, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + RETURN_THROWS(); } if (memlimit < crypto_pwhash_MEMLIMIT_MIN) { zend_argument_error(sodium_exception_ce, 3, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + RETURN_THROWS(); } hash_str = zend_string_alloc(crypto_pwhash_STRBYTES - 1, 0); if (crypto_pwhash_str @@ -1636,9 +1639,11 @@ PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256) } if (opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE) { zend_argument_error(sodium_exception_ce, 4, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + RETURN_THROWS(); } if (memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) { zend_argument_error(sodium_exception_ce, 5, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + RETURN_THROWS(); } hash = zend_string_alloc((size_t) hash_len, 0); if (crypto_pwhash_scryptsalsa208sha256 @@ -1681,9 +1686,11 @@ PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256_str) } if (opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE) { zend_argument_error(sodium_exception_ce, 2, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + RETURN_THROWS(); } if (memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) { zend_argument_error(sodium_exception_ce, 3, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + RETURN_THROWS(); } hash_str = zend_string_alloc (crypto_pwhash_scryptsalsa208sha256_STRBYTES - 1, 0); diff --git a/ext/sodium/tests/pwhash_memlimit_below_min.phpt b/ext/sodium/tests/pwhash_memlimit_below_min.phpt new file mode 100644 index 000000000000..63bf4443939b --- /dev/null +++ b/ext/sodium/tests/pwhash_memlimit_below_min.phpt @@ -0,0 +1,27 @@ +--TEST-- +sodium_crypto_pwhash(): a below-minimum memlimit reports a precise argument error +--EXTENSIONS-- +sodium +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} + +try { + sodium_crypto_pwhash_str("password", SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, 1); +} catch (SodiumException $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECTF-- +sodium_crypto_pwhash(): Argument #5 ($memlimit) must be greater than or equal to %d +sodium_crypto_pwhash_str(): Argument #3 ($memlimit) must be greater than or equal to %d diff --git a/ext/standard/filters.c b/ext/standard/filters.c index d26429704e26..c0741b46074e 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -1575,7 +1575,7 @@ static php_stream_filter_status_t strfilter_convert_filter( php_stream_bucket_delref(bucket); } - if (flags != PSFS_FLAG_NORMAL) { + if (flags & PSFS_FLAG_FLUSH_CLOSE) { if (strfilter_convert_append_bucket(inst, stream, thisfilter, buckets_out, NULL, 0, &consumed, php_stream_is_persistent(stream)) != SUCCESS) { diff --git a/ext/standard/tests/assert/gh22373.phpt b/ext/standard/tests/assert/gh22373.phpt new file mode 100644 index 000000000000..8c26f77f490b --- /dev/null +++ b/ext/standard/tests/assert/gh22373.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-22373: AST pretty-printing drops meaningful parentheses surrounding property access +--FILE-- +f)('abc') !== 'cba'); + } catch (Error $e) { + echo $e->getMessage(), PHP_EOL; + } + try { + assert(($this?->f)('abc') !== 'cba'); + } catch (Error $e) { + echo $e->getMessage(), PHP_EOL; + } + try { + assert((self::$sf)('abc') !== 'cba'); + } catch (Error $e) { + echo $e->getMessage(), PHP_EOL; + } + } +} + +new Foo(); + +?> +--EXPECT-- +assert(($this->f)('abc') !== 'cba') +assert(($this?->f)('abc') !== 'cba') +assert((self::$sf)('abc') !== 'cba') diff --git a/ext/standard/tests/filters/gh22360.phpt b/ext/standard/tests/filters/gh22360.phpt new file mode 100644 index 000000000000..b23483b22b92 --- /dev/null +++ b/ext/standard/tests/filters/gh22360.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-22360 (convert.base64-encode emits padding on incremental flush) +--FILE-- + +--CLEAN-- + +--EXPECT-- +string(4) "YWJj" +YWJj