Skip to content

Commit

Permalink
Add support for verifying optimizer func info
Browse files Browse the repository at this point in the history
This is guarded by -DZEND_VERIFY_FUNC_INFO=1. Enable this on the
variation job.

Closes GH-6924.
  • Loading branch information
nikic committed Jul 21, 2021
1 parent 11b990f commit 7b85d3b
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 11 deletions.
17 changes: 11 additions & 6 deletions Zend/Optimizer/zend_func_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -803,22 +803,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;
}
Expand All @@ -834,7 +839,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;
Expand Down
2 changes: 2 additions & 0 deletions Zend/Optimizer/zend_func_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 0 additions & 4 deletions Zend/Optimizer/zend_inference.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
63 changes: 63 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 0
#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)
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_type_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
10 changes: 10 additions & 0 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,4 @@ jobs:
configurationName: VARIATION_DEBUG_ZTS
configurationParameters: >-
--enable-debug --enable-zts
CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0"
CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1"

0 comments on commit 7b85d3b

Please sign in to comment.