diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c index 82f05a0a0175f..13801af0906d8 100644 --- a/Zend/Optimizer/zend_func_info.c +++ b/Zend/Optimizer/zend_func_info.c @@ -17,6 +17,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_API.h" #include "zend_compile.h" #include "zend_extensions.h" #include "zend_ssa.h" @@ -803,22 +804,27 @@ static const func_info_t func_infos[] = { static HashTable func_info; ZEND_API int zend_func_info_rid = -1; -static uint32_t get_internal_func_info( - const zend_call_info *call_info, const zend_ssa *ssa) { - zend_function *callee_func = call_info->callee_func; +uint32_t zend_get_internal_func_info( + const zend_function *callee_func, const zend_call_info *call_info, const zend_ssa *ssa) { if (callee_func->common.scope) { /* This is a method, not a function. */ return 0; } - zval *zv = zend_hash_find_known_hash(&func_info, callee_func->common.function_name); + zend_string *name = callee_func->common.function_name; + if (!name) { + /* zend_pass_function has no name. */ + return 0; + } + + zval *zv = zend_hash_find_known_hash(&func_info, name); if (!zv) { return 0; } func_info_t *info = Z_PTR_P(zv); if (info->info_func) { - return info->info_func(call_info, ssa); + return call_info ? info->info_func(call_info, ssa) : 0; } else { return info->info; } @@ -834,7 +840,7 @@ ZEND_API uint32_t zend_get_func_info( *ce_is_instanceof = 0; if (callee_func->type == ZEND_INTERNAL_FUNCTION) { - uint32_t internal_ret = get_internal_func_info(call_info, ssa); + uint32_t internal_ret = zend_get_internal_func_info(callee_func, call_info, ssa); #if !ZEND_DEBUG if (internal_ret) { return internal_ret; diff --git a/Zend/Optimizer/zend_func_info.h b/Zend/Optimizer/zend_func_info.h index 7a114154008fa..0a48edeefc4ce 100644 --- a/Zend/Optimizer/zend_func_info.h +++ b/Zend/Optimizer/zend_func_info.h @@ -56,6 +56,8 @@ BEGIN_EXTERN_C() extern ZEND_API int zend_func_info_rid; +uint32_t zend_get_internal_func_info( + const zend_function *callee_func, const zend_call_info *call_info, const zend_ssa *ssa); ZEND_API uint32_t zend_get_func_info( const zend_call_info *call_info, const zend_ssa *ssa, zend_class_entry **ce, bool *ce_is_instanceof); diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h index f9dbb405cc23c..aa968fb73552e 100644 --- a/Zend/Optimizer/zend_inference.h +++ b/Zend/Optimizer/zend_inference.h @@ -31,10 +31,6 @@ #define MAY_BE_GUARD (1<<28) /* needs type guard */ //#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */ -//TODO: remome MAY_BE_RC1, MAY_BE_RCN??? -#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */ -#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */ - #define MAY_HAVE_DTOR \ (MAY_BE_OBJECT|MAY_BE_RESOURCE \ |MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index b7aa91b5f9c3a..daa187d1ff8a2 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -42,6 +42,7 @@ #include "zend_smart_str.h" #include "zend_observer.h" #include "zend_system_id.h" +#include "Optimizer/zend_func_info.h" /* Virtual current working directory support */ #include "zend_virtual_cwd.h" @@ -1199,6 +1200,68 @@ static void zend_verify_internal_read_property_type(zend_object *obj, zend_strin zend_verify_property_type(prop_info, val, /* strict */ true); } } + +#ifndef ZEND_VERIFY_FUNC_INFO +# define ZEND_VERIFY_FUNC_INFO 1 +#endif + +static void zend_verify_internal_func_info(zend_function *fn, zval *retval) { +#if ZEND_VERIFY_FUNC_INFO + zend_string *name = fn->common.function_name; + uint32_t type_mask = zend_get_internal_func_info(fn, NULL, NULL); + if (!type_mask) { + return; + } + + /* Always check refcount of arrays, as immutable arrays are RCN. */ + if (Z_REFCOUNTED_P(retval) || Z_TYPE_P(retval) == IS_ARRAY) { + if (!(type_mask & MAY_BE_RC1)) { + zend_error_noreturn(E_CORE_ERROR, "%s() missing rc1", ZSTR_VAL(name)); + } + if (Z_REFCOUNT_P(retval) > 1 && !(type_mask & MAY_BE_RCN)) { + zend_error_noreturn(E_CORE_ERROR, "%s() missing rcn", ZSTR_VAL(name)); + } + } + + uint32_t type = 1u << Z_TYPE_P(retval); + if (!(type_mask & type)) { + zend_error_noreturn(E_CORE_ERROR, "%s() missing type %s", + ZSTR_VAL(name), zend_get_type_by_const(Z_TYPE_P(retval))); + } + + if (Z_TYPE_P(retval) == IS_ARRAY) { + HashTable *ht = Z_ARRVAL_P(retval); + uint32_t num_checked = 0; + zend_string *str; + zval *val; + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) { + if (str) { + if (!(type_mask & MAY_BE_ARRAY_KEY_STRING)) { + zend_error_noreturn(E_CORE_ERROR, + "%s() missing array_key_string", ZSTR_VAL(name)); + } + } else { + if (!(type_mask & MAY_BE_ARRAY_KEY_LONG)) { + zend_error_noreturn(E_CORE_ERROR, + "%s() missing array_key_long", ZSTR_VAL(name)); + } + } + + uint32_t array_type = 1u << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT); + if (!(type_mask & array_type)) { + zend_error_noreturn(E_CORE_ERROR, + "%s() missing array element type %s", + ZSTR_VAL(name), zend_get_type_by_const(Z_TYPE_P(retval))); + } + + /* Don't check all elements of large arrays. */ + if (++num_checked > 16) { + break; + } + } ZEND_HASH_FOREACH_END(); + } +#endif +} #endif ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data) diff --git a/Zend/zend_type_info.h b/Zend/zend_type_info.h index dc623a68f7bbc..b61b09d296eeb 100644 --- a/Zend/zend_type_info.h +++ b/Zend/zend_type_info.h @@ -67,6 +67,9 @@ #define MAY_BE_CLASS (1<<24) #define MAY_BE_INDIRECT (1<<25) +#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */ +#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */ + #define MAY_BE_ANY_ARRAY \ (MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 20b3b29916a39..696d6959e993e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3905,6 +3905,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL)) zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -4023,6 +4024,7 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER)) zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -4132,6 +4134,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -8639,6 +8642,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER)) zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index c8bc4d5f42a4f..cee8248567c6a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1243,6 +1243,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -1304,6 +1305,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -1469,6 +1471,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -1563,6 +1566,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -1658,6 +1662,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_ zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -1767,6 +1772,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -1875,6 +1881,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -1983,6 +1990,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -3338,6 +3346,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif @@ -3474,6 +3483,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ zend_verify_internal_return_type(call->func, ret)); ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); } #endif