From 5ef138b0c7c4e9532e205f45c18a72aa1d279c24 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Sun, 23 Nov 2014 21:09:31 +0100 Subject: [PATCH] Fix bug #68446 (bug with constant defaults and type hints) --- Zend/tests/class_constants_002.phpt | 1 - Zend/zend_compile.c | 8 ++--- Zend/zend_execute.c | 18 ++++++++--- Zend/zend_vm_def.h | 50 ++++++++++++++++++----------- Zend/zend_vm_execute.h | 50 ++++++++++++++++++----------- 5 files changed, 80 insertions(+), 47 deletions(-) diff --git a/Zend/tests/class_constants_002.phpt b/Zend/tests/class_constants_002.phpt index 9aad8088da..75d1032e54 100644 --- a/Zend/tests/class_constants_002.phpt +++ b/Zend/tests/class_constants_002.phpt @@ -26,6 +26,5 @@ echo "Done\n"; --EXPECTF-- int(1) int(5) -int(10) Fatal error: Class 'NoSuchClass' not found in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8e416ea0a5..e023b24a8e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1931,8 +1931,8 @@ void zend_do_receive_param(zend_uchar op, znode *varname, const znode *initializ if (class_type->u.constant.type == IS_ARRAY) { cur_arg_info->type_hint = IS_ARRAY; if (op == ZEND_RECV_INIT) { - if (Z_TYPE(initialization->u.constant) == IS_NULL || (Z_TYPE(initialization->u.constant) == IS_CONSTANT && !strcasecmp(Z_STRVAL(initialization->u.constant), "NULL")) || Z_TYPE(initialization->u.constant) == IS_CONSTANT_AST) { - cur_arg_info->allow_null = 1; + if (Z_TYPE(initialization->u.constant) == IS_NULL || (Z_TYPE(initialization->u.constant) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT || (Z_TYPE(initialization->u.constant) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT_AST) { + cur_arg_info->allow_null = (Z_TYPE(initialization->u.constant) != IS_NULL) + 1; } else if (Z_TYPE(initialization->u.constant) != IS_ARRAY) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters with array type hint can only be an array or NULL"); } @@ -1940,8 +1940,8 @@ void zend_do_receive_param(zend_uchar op, znode *varname, const znode *initializ } else if (class_type->u.constant.type == IS_CALLABLE) { cur_arg_info->type_hint = IS_CALLABLE; if (op == ZEND_RECV_INIT) { - if (Z_TYPE(initialization->u.constant) == IS_NULL || (Z_TYPE(initialization->u.constant) == IS_CONSTANT && !strcasecmp(Z_STRVAL(initialization->u.constant), "NULL")) || Z_TYPE(initialization->u.constant) == IS_CONSTANT_AST) { - cur_arg_info->allow_null = 1; + if (Z_TYPE(initialization->u.constant) == IS_NULL || (Z_TYPE(initialization->u.constant) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT || (Z_TYPE(initialization->u.constant) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT_AST) { + cur_arg_info->allow_null = (Z_TYPE(initialization->u.constant) != IS_NULL) + 1; } else { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters with callable type hint can only be NULL"); } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 6ada04e1cb..081af81d50 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -608,7 +608,17 @@ ZEND_API int zend_verify_arg_error(int error_type, const zend_function *zf, zend return 0; } -static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, ulong fetch_type TSRMLS_DC) +static inline int zend_arg_allows_null(zend_bool allow_null, zval *default_value TSRMLS_DC) +{ + if (allow_null < 2 || !default_value) { + return allow_null; + } + + /* assuming update_constant_ex done before */ + return Z_TYPE_P(default_value) == IS_NULL; +} + +static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, zval *defaultval, ulong fetch_type TSRMLS_DC) { zend_arg_info *cur_arg_info; char *need_msg; @@ -638,7 +648,7 @@ static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zva if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) { return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC); } - } else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { + } else if (Z_TYPE_P(arg) != IS_NULL || !zend_arg_allows_null(cur_arg_info->allow_null, defaultval TSRMLS_CC)) { need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC); } @@ -649,7 +659,7 @@ static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zva return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type array", "", "none", "" TSRMLS_CC); } - if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { + if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !zend_arg_allows_null(cur_arg_info->allow_null, defaultval TSRMLS_CC))) { return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type array", "", zend_zval_type_name(arg), "" TSRMLS_CC); } break; @@ -658,7 +668,7 @@ static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zva if (!arg) { return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be callable", "", "none", "" TSRMLS_CC); } - if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL TSRMLS_CC) && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { + if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL TSRMLS_CC) && (Z_TYPE_P(arg) != IS_NULL || !zend_arg_allows_null(cur_arg_info->allow_null, defaultval TSRMLS_CC))) { return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be callable", "", zend_zval_type_name(arg), "" TSRMLS_CC); } break; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 705e46e7d3..65981f6f6f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1961,7 +1961,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) void **p = EX(function_state).arguments - num_args; for (i = 0; i < num_args; ++i, ++p) { - zend_verify_arg_type(fbc, i + 1, (zval *) *p, 0 TSRMLS_CC); + zend_verify_arg_type(fbc, i + 1, (zval *) *p, NULL, 0 TSRMLS_CC); } } @@ -3374,7 +3374,7 @@ ZEND_VM_HANDLER(63, ZEND_RECV, ANY, ANY) SAVE_OPLINE(); if (UNEXPECTED(param == NULL)) { - if (zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, NULL, opline->extended_value TSRMLS_CC)) { + if (zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, NULL, NULL, opline->extended_value TSRMLS_CC)) { const char *space; const char *class_name; zend_execute_data *ptr; @@ -3396,7 +3396,7 @@ ZEND_VM_HANDLER(63, ZEND_RECV, ANY, ANY) } else { zval **var_ptr; - zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC); + zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, NULL, opline->extended_value TSRMLS_CC); var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->result.var TSRMLS_CC); Z_DELREF_PP(var_ptr); *var_ptr = *param; @@ -3410,27 +3410,34 @@ ZEND_VM_HANDLER(63, ZEND_RECV, ANY, ANY) ZEND_VM_HANDLER(64, ZEND_RECV_INIT, ANY, CONST) { USE_OPLINE - zval *assignment_value; + zval *assignment_value, *default_val = NULL; zend_uint arg_num = opline->op1.num; zval **param = zend_vm_stack_get_arg(arg_num TSRMLS_CC); zval **var_ptr; SAVE_OPLINE(); + if (IS_CONSTANT_TYPE(Z_TYPE_P(opline->op2.zv))) { + ALLOC_ZVAL(default_val); + *default_val = *opline->op2.zv; + Z_SET_REFCOUNT_P(default_val, 1); + zval_update_constant(&default_val, 0 TSRMLS_CC); + } if (param == NULL) { - ALLOC_ZVAL(assignment_value); - *assignment_value = *opline->op2.zv; - if (IS_CONSTANT_TYPE(Z_TYPE_P(assignment_value))) { - Z_SET_REFCOUNT_P(assignment_value, 1); - zval_update_constant(&assignment_value, 0 TSRMLS_CC); - } else if (Z_TYPE_P(assignment_value) == IS_ARRAY) { - HashTable *ht; - - ALLOC_HASHTABLE(ht); - zend_hash_init(ht, zend_hash_num_elements(Z_ARRVAL_P(assignment_value)), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(ht, Z_ARRVAL_P(assignment_value), (copy_ctor_func_t) zval_deep_copy, NULL, sizeof(zval *)); - Z_ARRVAL_P(assignment_value) = ht; + if (default_val) { + assignment_value = default_val; } else { - zval_copy_ctor(assignment_value); + ALLOC_ZVAL(assignment_value); + *assignment_value = *opline->op2.zv; + if (Z_TYPE_P(assignment_value) == IS_ARRAY) { + HashTable *ht; + + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, zend_hash_num_elements(Z_ARRVAL_P(assignment_value)), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(ht, Z_ARRVAL_P(assignment_value), (copy_ctor_func_t) zval_deep_copy, NULL, sizeof(zval *)); + Z_ARRVAL_P(assignment_value) = ht; + } else { + zval_copy_ctor(assignment_value); + } } INIT_PZVAL(assignment_value); } else { @@ -3438,11 +3445,16 @@ ZEND_VM_HANDLER(64, ZEND_RECV_INIT, ANY, CONST) Z_ADDREF_P(assignment_value); } - zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, assignment_value, opline->extended_value TSRMLS_CC); + zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, assignment_value, default_val, opline->extended_value TSRMLS_CC); var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->result.var TSRMLS_CC); zval_ptr_dtor(var_ptr); *var_ptr = assignment_value; + if (default_val && assignment_value != default_val) { + zval_dtor(default_val); + efree(default_val); + } + CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -3469,7 +3481,7 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, ANY, ANY) for (; arg_num <= arg_count; ++arg_num) { zval **param = zend_vm_stack_get_arg(arg_num TSRMLS_CC); - zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC); + zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, NULL, opline->extended_value TSRMLS_CC); zend_hash_next_index_insert(Z_ARRVAL_P(params), param, sizeof(zval *), NULL); Z_ADDREF_PP(param); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 993aaee747..318f903f77 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -541,7 +541,7 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR void **p = EX(function_state).arguments - num_args; for (i = 0; i < num_args; ++i, ++p) { - zend_verify_arg_type(fbc, i + 1, (zval *) *p, 0 TSRMLS_CC); + zend_verify_arg_type(fbc, i + 1, (zval *) *p, NULL, 0 TSRMLS_CC); } } @@ -861,7 +861,7 @@ static int ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) SAVE_OPLINE(); if (UNEXPECTED(param == NULL)) { - if (zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, NULL, opline->extended_value TSRMLS_CC)) { + if (zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, NULL, NULL, opline->extended_value TSRMLS_CC)) { const char *space; const char *class_name; zend_execute_data *ptr; @@ -883,7 +883,7 @@ static int ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } else { zval **var_ptr; - zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC); + zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, NULL, opline->extended_value TSRMLS_CC); var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->result.var TSRMLS_CC); Z_DELREF_PP(var_ptr); *var_ptr = *param; @@ -916,7 +916,7 @@ static int ZEND_FASTCALL ZEND_RECV_VARIADIC_SPEC_HANDLER(ZEND_OPCODE_HANDLER_AR for (; arg_num <= arg_count; ++arg_num) { zval **param = zend_vm_stack_get_arg(arg_num TSRMLS_CC); - zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC); + zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, NULL, opline->extended_value TSRMLS_CC); zend_hash_next_index_insert(Z_ARRVAL_P(params), param, sizeof(zval *), NULL); Z_ADDREF_PP(param); } @@ -1615,27 +1615,34 @@ static int ZEND_FASTCALL ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPC static int ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *assignment_value; + zval *assignment_value, *default_val = NULL; zend_uint arg_num = opline->op1.num; zval **param = zend_vm_stack_get_arg(arg_num TSRMLS_CC); zval **var_ptr; SAVE_OPLINE(); + if (IS_CONSTANT_TYPE(Z_TYPE_P(opline->op2.zv))) { + ALLOC_ZVAL(default_val); + *default_val = *opline->op2.zv; + Z_SET_REFCOUNT_P(default_val, 1); + zval_update_constant(&default_val, 0 TSRMLS_CC); + } if (param == NULL) { - ALLOC_ZVAL(assignment_value); - *assignment_value = *opline->op2.zv; - if (IS_CONSTANT_TYPE(Z_TYPE_P(assignment_value))) { - Z_SET_REFCOUNT_P(assignment_value, 1); - zval_update_constant(&assignment_value, 0 TSRMLS_CC); - } else if (Z_TYPE_P(assignment_value) == IS_ARRAY) { - HashTable *ht; - - ALLOC_HASHTABLE(ht); - zend_hash_init(ht, zend_hash_num_elements(Z_ARRVAL_P(assignment_value)), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(ht, Z_ARRVAL_P(assignment_value), (copy_ctor_func_t) zval_deep_copy, NULL, sizeof(zval *)); - Z_ARRVAL_P(assignment_value) = ht; + if (default_val) { + assignment_value = default_val; } else { - zval_copy_ctor(assignment_value); + ALLOC_ZVAL(assignment_value); + *assignment_value = *opline->op2.zv; + if (Z_TYPE_P(assignment_value) == IS_ARRAY) { + HashTable *ht; + + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, zend_hash_num_elements(Z_ARRVAL_P(assignment_value)), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(ht, Z_ARRVAL_P(assignment_value), (copy_ctor_func_t) zval_deep_copy, NULL, sizeof(zval *)); + Z_ARRVAL_P(assignment_value) = ht; + } else { + zval_copy_ctor(assignment_value); + } } INIT_PZVAL(assignment_value); } else { @@ -1643,11 +1650,16 @@ static int ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ Z_ADDREF_P(assignment_value); } - zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, assignment_value, opline->extended_value TSRMLS_CC); + zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, assignment_value, default_val, opline->extended_value TSRMLS_CC); var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->result.var TSRMLS_CC); zval_ptr_dtor(var_ptr); *var_ptr = assignment_value; + if (default_val && assignment_value != default_val) { + zval_dtor(default_val); + efree(default_val); + } + CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } -- 2.40.0