diff --git a/NEWS b/NEWS index 2a266243a747..705ff85b2a6e 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ PHP NEWS - Core: . Sync Boost.Context assembly with 1.91.0. (kn1g78) + . Fixed bug GH-22387 (AST pretty-printing drops meaningful parentheses around + RHS of instanceof). (timwolla) - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index a8eb6b805413..adfc91880f29 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -120,6 +120,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Added ZEND_CONTAINER_OF(). . The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead use php_check_open_basedir() directly. + . The Z_CONSTANT(), Z_CONSTANT_P(), Z_OPT_CONSTANT(), and + Z_OPT_CONSTANT_P() macros have been removed. Check for IS_CONSTANT_AST + directly. . Added zend_reflection_property_set_raw_value_without_lazy_initialization(), zend_reflection_property_set_raw_value() to expose the functionality of ReflectionProperty::setRawValueWithoutLazyInitialization() and diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index d3ce419c737e..3aa04de860a1 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1686,6 +1686,19 @@ static ZEND_COLD void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int } zend_ast_export_ex(str, ast, priority, indent); } +static ZEND_COLD void zend_ast_export_ns_name_or_expression(smart_str *str, zend_ast *ast, int priority, int indent) +{ + switch (ast->kind) { + case ZEND_AST_ZVAL: + case ZEND_AST_VAR: + zend_ast_export_ns_name(str, ast, priority, indent); + break; + default: + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast, priority, indent); + smart_str_appendc(str, ')'); + } +} static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len) { @@ -2523,25 +2536,12 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_var(str, ast->child[1], indent); break; case ZEND_AST_STATIC_PROP: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appends(str, "::$"); zend_ast_export_var(str, ast->child[1], indent); break; case ZEND_AST_CALL: { - zend_ast *left = ast->child[0]; - 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; - } + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2553,7 +2553,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio goto simple_list; } case ZEND_AST_CLASS_CONST: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_name(str, ast->child[1], 0, indent); break; @@ -2570,7 +2570,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio default: ZEND_UNREACHABLE(); } } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); } smart_str_appends(str, "::class"); break; @@ -2649,7 +2649,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio } zend_ast_export_class_no_header(str, decl, indent); } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2658,7 +2658,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_INSTANCEOF: zend_ast_export_ex(str, ast->child[0], 0, indent); smart_str_appends(str, " instanceof "); - zend_ast_export_ns_name(str, ast->child[1], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[1], 0, indent); break; case ZEND_AST_YIELD: if (priority > 70) smart_str_appendc(str, '('); @@ -2851,7 +2851,12 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appendc(str, ')'); break; case ZEND_AST_STATIC_CALL: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + if (zend_ast_is_parent_hook_call(ast)) { + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + } else { + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); + } + smart_str_appends(str, "::"); zend_ast_export_var(str, ast->child[1], indent); smart_str_appendc(str, '('); @@ -3082,3 +3087,22 @@ zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast) ZEND_UNREACHABLE(); return NULL; } + +bool zend_ast_is_parent_hook_call(const zend_ast *ast) +{ + ZEND_ASSERT(ast->kind == ZEND_AST_STATIC_CALL); + + const zend_ast *class_ast = ast->child[0]; + zend_ast *method_ast = ast->child[1]; + + return class_ast->kind == ZEND_AST_STATIC_PROP + && !(class_ast->attr & ZEND_PARENTHESIZED_STATIC_PROP) + && class_ast->child[0]->kind == ZEND_AST_ZVAL + && Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) == IS_STRING + && zend_get_class_fetch_type(zend_ast_get_str(class_ast->child[0])) == ZEND_FETCH_CLASS_PARENT + && class_ast->child[1]->kind == ZEND_AST_ZVAL + && method_ast->kind == ZEND_AST_ZVAL + && Z_TYPE_P(zend_ast_get_zval(method_ast)) == IS_STRING + && (zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "get") + || zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "set")); +} diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 24b77d7d3493..86a68c1cbaf9 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -440,4 +440,7 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast); +/* Recognize parent::$prop::get() pattern. */ +bool zend_ast_is_parent_hook_call(const zend_ast *ast); + #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d9fa3a1cb504..ac5a9d71a6ea 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5353,17 +5353,7 @@ static bool zend_compile_parent_property_hook_call(znode *result, const zend_ast const zend_ast *class_ast = ast->child[0]; zend_ast *method_ast = ast->child[1]; - /* Recognize parent::$prop::get() pattern. */ - if (class_ast->kind != ZEND_AST_STATIC_PROP - || (class_ast->attr & ZEND_PARENTHESIZED_STATIC_PROP) - || class_ast->child[0]->kind != ZEND_AST_ZVAL - || Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) != IS_STRING - || zend_get_class_fetch_type(zend_ast_get_str(class_ast->child[0])) != ZEND_FETCH_CLASS_PARENT - || class_ast->child[1]->kind != ZEND_AST_ZVAL - || method_ast->kind != ZEND_AST_ZVAL - || Z_TYPE_P(zend_ast_get_zval(method_ast)) != IS_STRING - || (!zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "get") - && !zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "set"))) { + if (!zend_ast_is_parent_hook_call(ast)) { return false; } @@ -9224,7 +9214,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f if (*value_ast_ptr) { zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false); - if (ZEND_TYPE_IS_SET(type) && !Z_CONSTANT(value_zv) + if (ZEND_TYPE_IS_SET(type) && Z_TYPE(value_zv) != IS_CONSTANT_AST && !zend_is_valid_default_value(type, &value_zv)) { zend_string *str = zend_type_to_string(type); if (Z_TYPE(value_zv) == IS_NULL && !ZEND_TYPE_IS_INTERSECTION(type)) { @@ -9348,7 +9338,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false); - if (!Z_CONSTANT(value_zv) && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) { + if (Z_TYPE(value_zv) != IS_CONSTANT_AST && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) { zend_string *type_str = zend_type_to_string(type); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as value for class constant %s::%s of type %s", diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y index d35853ab5acd..47b05de4e692 100644 --- a/Zend/zend_ini_parser.y +++ b/Zend/zend_ini_parser.y @@ -151,7 +151,7 @@ static void zend_ini_get_constant(zval *result, zval *name) && (c = zend_get_constant(Z_STR_P(name))) != 0) { if (Z_TYPE_P(c) != IS_STRING) { ZVAL_COPY_OR_DUP(&tmp, c); - if (Z_OPT_CONSTANT(tmp)) { + if (Z_OPT_TYPE(tmp) == IS_CONSTANT_AST) { zval_update_constant_ex(&tmp, NULL); } convert_to_string(&tmp); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 2cdfdcb1961f..56774e4e9d8f 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -1017,8 +1017,6 @@ static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint3 } while(0) /* All data types < IS_STRING have their constructor/destructors skipped */ -#define Z_CONSTANT(zval) (Z_TYPE(zval) == IS_CONSTANT_AST) -#define Z_CONSTANT_P(zval_p) Z_CONSTANT(*(zval_p)) #if 1 /* This optimized version assumes that we have a single "type_flag" */ @@ -1036,9 +1034,6 @@ static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint3 #define Z_OPT_TYPE(zval) (Z_TYPE_INFO(zval) & Z_TYPE_MASK) #define Z_OPT_TYPE_P(zval_p) Z_OPT_TYPE(*(zval_p)) -#define Z_OPT_CONSTANT(zval) (Z_OPT_TYPE(zval) == IS_CONSTANT_AST) -#define Z_OPT_CONSTANT_P(zval_p) Z_OPT_CONSTANT(*(zval_p)) - #define Z_OPT_REFCOUNTED(zval) Z_TYPE_INFO_REFCOUNTED(Z_TYPE_INFO(zval)) #define Z_OPT_REFCOUNTED_P(zval_p) Z_OPT_REFCOUNTED(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a0bfad488f85..ec9a97eeb254 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8323,7 +8323,7 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST) val = GET_OP2_ZVAL_PTR(BP_VAR_R); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); FREE_OP1(); @@ -8355,7 +8355,7 @@ ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST) val = GET_OP2_ZVAL_PTR(BP_VAR_R); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); FREE_OP1(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index cedc735bbb1e..34b2455ce439 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -8290,7 +8290,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_CONST val = RT_CONSTANT(opline, opline->op2); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); @@ -8325,7 +8325,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_ATTRI val = RT_CONSTANT(opline, opline->op2); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); @@ -60979,7 +60979,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_CONST_SPEC val = RT_CONSTANT(opline, opline->op2); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); @@ -61014,7 +61014,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_ATTRIBUTED val = RT_CONSTANT(opline, opline->op2); ZVAL_COPY(&c.value, val); - if (Z_OPT_CONSTANT(c.value)) { + if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) { zval_ptr_dtor_nogc(&c.value); diff --git a/ext/iconv/config.m4 b/ext/iconv/config.m4 index e86f3535a97c..bea97b27f4f7 100644 --- a/ext/iconv/config.m4 +++ b/ext/iconv/config.m4 @@ -112,6 +112,8 @@ int main(void) { char *out = malloc(out_left); char *out_p = out; size_t result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left); + free(out); + iconv_close(cd); if(result == (size_t)-1) { return 1; } diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index d62ef95b5513..b0b04456706b 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10924,7 +10924,7 @@ static int zend_jit_recv_init(zend_jit_ctx *jit, const zend_op *opline, const ze zv, true); } - if (Z_CONSTANT_P(zv)) { + if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { jit_SET_EX_OPLINE(jit, opline); ref = ir_CALL_2(IR_I32, ir_CONST_FC_FUNC(zval_update_constant_ex), jit_ZVAL_ADDR(jit, res_addr), diff --git a/ext/standard/tests/assert/gh22387.phpt b/ext/standard/tests/assert/gh22387.phpt new file mode 100644 index 000000000000..38c93921608a --- /dev/null +++ b/ext/standard/tests/assert/gh22387.phpt @@ -0,0 +1,62 @@ +--TEST-- +GH-22387: AST pretty-printing drops meaningful parentheses around RHS of instanceof +--FILE-- +getMessage(), PHP_EOL; +} + +try { + assert(!new (bar)()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::m()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::$p); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::C); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert((baz)::class !== 'stdClass'); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +assert(!$foo instanceof (bar)) +assert(!new (bar)()) +assert(!(bar)::m()) +assert(!(bar)::$p) +assert(!(bar)::C) +assert((baz)::class !== 'stdClass')