From a0e2420f98568d197c6530b5a6db7dc392b48df5 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 2 Feb 2023 15:40:50 +0100 Subject: [PATCH] Fix comp-time and constant evaluation of dynamic class constant fetch Fixes GH-10486 Fixes oss-fuzz #55436 Fixes oss-fuzz #55472 --- Zend/Optimizer/compact_literals.c | 4 +++- Zend/tests/gh10486.phpt | 11 +++++++++++ Zend/tests/gh10486_2.phpt | 11 +++++++++++ Zend/zend_ast.c | 12 +++++++++++- Zend/zend_compile.c | 5 +++++ Zend/zend_execute.c | 5 +++++ Zend/zend_execute.h | 2 ++ Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 12 ++++++------ 9 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/gh10486.phpt create mode 100644 Zend/tests/gh10486_2.phpt diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index 94402f026c77..9248f0b82244 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -670,7 +670,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; case ZEND_FETCH_CLASS_CONSTANT: - if (opline->op1_type == IS_CONST && opline->op2_type == IS_CONST) { + if (opline->op1_type == IS_CONST + && opline->op2_type == IS_CONST + && Z_TYPE(op_array->literals[opline->op2.constant]) == IS_STRING) { // op1/op2 class_const opline->extended_value = add_static_slot(&hash, op_array, opline->op1.constant, diff --git a/Zend/tests/gh10486.phpt b/Zend/tests/gh10486.phpt new file mode 100644 index 000000000000..83dff538926d --- /dev/null +++ b/Zend/tests/gh10486.phpt @@ -0,0 +1,11 @@ +--TEST-- +Assertion error when attempting comp-time eval of dynamic class constant fetch +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Class "y" not found in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh10486_2.phpt b/Zend/tests/gh10486_2.phpt new file mode 100644 index 000000000000..d62215a7771a --- /dev/null +++ b/Zend/tests/gh10486_2.phpt @@ -0,0 +1,11 @@ +--TEST-- +Assertion error when attempting constant eval of dynamic class constant fetch +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Undefined constant "y" in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 70646856d3a1..773dab1b9e19 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -803,7 +803,15 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex( case ZEND_AST_CLASS_CONST: { zend_string *class_name = zend_ast_get_str(ast->child[0]); - zend_string *const_name = zend_ast_get_str(ast->child[1]); + if (UNEXPECTED(zend_ast_evaluate_ex(&op2, ast->child[1], scope, &short_circuited, ctx) != SUCCESS)) { + return FAILURE; + } + if (UNEXPECTED(Z_TYPE(op2) != IS_STRING)) { + zend_invalid_class_constant_type_error(Z_TYPE(op2)); + zval_ptr_dtor_nogc(&op2); + return FAILURE; + } + zend_string *const_name = Z_STR(op2); zend_string *previous_filename; zend_long previous_lineno; @@ -821,9 +829,11 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex( if (UNEXPECTED(zv == NULL)) { ZVAL_UNDEF(result); + zval_ptr_dtor_nogc(&op2); return FAILURE; } ZVAL_COPY_OR_DUP(result, zv); + zval_ptr_dtor_nogc(&op2); break; } case ZEND_AST_NEW: diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c2d53bd943ec..dcadf6eef6d0 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -10742,6 +10742,11 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ zend_ast *name_ast; zend_string *resolved_name; + if (UNEXPECTED(ast->child[1]->kind != ZEND_AST_ZVAL + || Z_TYPE_P(zend_ast_get_zval(ast->child[1])) != IS_STRING)) { + return; + } + zend_eval_const_expr(&ast->child[0]); zend_eval_const_expr(&ast->child[1]); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index b051937a5bc0..3a367bcc09c6 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -894,6 +894,11 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_readonly_property_indirect_modificati ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name)); } +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_invalid_class_constant_type_error(zend_uchar type) +{ + zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(type)); +} + static const zend_class_entry *resolve_single_class_type(zend_string *name, const zend_class_entry *self_ce) { if (zend_string_equals_literal_ci(name, "self")) { return self_ce; diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index a1e29f5cd0a0..f02c45f5084e 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -82,6 +82,8 @@ ZEND_API ZEND_COLD void zend_wrong_string_offset_error(void); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_readonly_property_modification_error(const zend_property_info *info); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_readonly_property_indirect_modification_error(const zend_property_info *info); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_invalid_class_constant_type_error(zend_uchar type); + ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg); ZEND_API ZEND_COLD void zend_verify_arg_error( const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index efde56931d6a..ca062b9512ac 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5922,7 +5922,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO constant_zv = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R); if (UNEXPECTED(Z_TYPE_P(constant_zv) != IS_STRING)) { - zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(Z_TYPE_P(constant_zv))); + zend_invalid_class_constant_type_error(Z_TYPE_P(constant_zv)); ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP2(); HANDLE_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index f4e1b67cbd45..d9dd9b589024 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7203,7 +7203,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS constant_zv = RT_CONSTANT(opline, opline->op2); if (UNEXPECTED(Z_TYPE_P(constant_zv) != IS_STRING)) { - zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(Z_TYPE_P(constant_zv))); + zend_invalid_class_constant_type_error(Z_TYPE_P(constant_zv)); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -8358,7 +8358,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS constant_zv = _get_zval_ptr_tmpvarcv(opline->op2_type, opline->op2, BP_VAR_R EXECUTE_DATA_CC); if (UNEXPECTED(Z_TYPE_P(constant_zv) != IS_STRING)) { - zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(Z_TYPE_P(constant_zv))); + zend_invalid_class_constant_type_error(Z_TYPE_P(constant_zv)); ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); @@ -25011,7 +25011,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ constant_zv = RT_CONSTANT(opline, opline->op2); if (UNEXPECTED(Z_TYPE_P(constant_zv) != IS_STRING)) { - zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(Z_TYPE_P(constant_zv))); + zend_invalid_class_constant_type_error(Z_TYPE_P(constant_zv)); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -25574,7 +25574,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ constant_zv = _get_zval_ptr_tmpvarcv(opline->op2_type, opline->op2, BP_VAR_R EXECUTE_DATA_CC); if (UNEXPECTED(Z_TYPE_P(constant_zv) != IS_STRING)) { - zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(Z_TYPE_P(constant_zv))); + zend_invalid_class_constant_type_error(Z_TYPE_P(constant_zv)); ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION(); @@ -33999,7 +33999,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS constant_zv = RT_CONSTANT(opline, opline->op2); if (UNEXPECTED(Z_TYPE_P(constant_zv) != IS_STRING)) { - zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(Z_TYPE_P(constant_zv))); + zend_invalid_class_constant_type_error(Z_TYPE_P(constant_zv)); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -34352,7 +34352,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS constant_zv = _get_zval_ptr_tmpvarcv(opline->op2_type, opline->op2, BP_VAR_R EXECUTE_DATA_CC); if (UNEXPECTED(Z_TYPE_P(constant_zv) != IS_STRING)) { - zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(Z_TYPE_P(constant_zv))); + zend_invalid_class_constant_type_error(Z_TYPE_P(constant_zv)); ZVAL_UNDEF(EX_VAR(opline->result.var)); FREE_OP(opline->op2_type, opline->op2.var); HANDLE_EXCEPTION();