Skip to content

Commit 7b85d3b

Browse files
committed
Add support for verifying optimizer func info
This is guarded by -DZEND_VERIFY_FUNC_INFO=1. Enable this on the variation job. Closes GH-6924.
1 parent 11b990f commit 7b85d3b

File tree

8 files changed

+94
-11
lines changed

8 files changed

+94
-11
lines changed

Zend/Optimizer/zend_func_info.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -803,22 +803,27 @@ static const func_info_t func_infos[] = {
803803
static HashTable func_info;
804804
ZEND_API int zend_func_info_rid = -1;
805805

806-
static uint32_t get_internal_func_info(
807-
const zend_call_info *call_info, const zend_ssa *ssa) {
808-
zend_function *callee_func = call_info->callee_func;
806+
uint32_t zend_get_internal_func_info(
807+
const zend_function *callee_func, const zend_call_info *call_info, const zend_ssa *ssa) {
809808
if (callee_func->common.scope) {
810809
/* This is a method, not a function. */
811810
return 0;
812811
}
813812

814-
zval *zv = zend_hash_find_known_hash(&func_info, callee_func->common.function_name);
813+
zend_string *name = callee_func->common.function_name;
814+
if (!name) {
815+
/* zend_pass_function has no name. */
816+
return 0;
817+
}
818+
819+
zval *zv = zend_hash_find_known_hash(&func_info, name);
815820
if (!zv) {
816821
return 0;
817822
}
818823

819824
func_info_t *info = Z_PTR_P(zv);
820825
if (info->info_func) {
821-
return info->info_func(call_info, ssa);
826+
return call_info ? info->info_func(call_info, ssa) : 0;
822827
} else {
823828
return info->info;
824829
}
@@ -834,7 +839,7 @@ ZEND_API uint32_t zend_get_func_info(
834839
*ce_is_instanceof = 0;
835840

836841
if (callee_func->type == ZEND_INTERNAL_FUNCTION) {
837-
uint32_t internal_ret = get_internal_func_info(call_info, ssa);
842+
uint32_t internal_ret = zend_get_internal_func_info(callee_func, call_info, ssa);
838843
#if !ZEND_DEBUG
839844
if (internal_ret) {
840845
return internal_ret;

Zend/Optimizer/zend_func_info.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ BEGIN_EXTERN_C()
5656

5757
extern ZEND_API int zend_func_info_rid;
5858

59+
uint32_t zend_get_internal_func_info(
60+
const zend_function *callee_func, const zend_call_info *call_info, const zend_ssa *ssa);
5961
ZEND_API uint32_t zend_get_func_info(
6062
const zend_call_info *call_info, const zend_ssa *ssa,
6163
zend_class_entry **ce, bool *ce_is_instanceof);

Zend/Optimizer/zend_inference.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@
3131
#define MAY_BE_GUARD (1<<28) /* needs type guard */
3232
//#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */
3333

34-
//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
35-
#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */
36-
#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */
37-
3834
#define MAY_HAVE_DTOR \
3935
(MAY_BE_OBJECT|MAY_BE_RESOURCE \
4036
|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE)

Zend/zend_execute.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "zend_smart_str.h"
4343
#include "zend_observer.h"
4444
#include "zend_system_id.h"
45+
#include "Optimizer/zend_func_info.h"
4546

4647
/* Virtual current working directory support */
4748
#include "zend_virtual_cwd.h"
@@ -1199,6 +1200,68 @@ static void zend_verify_internal_read_property_type(zend_object *obj, zend_strin
11991200
zend_verify_property_type(prop_info, val, /* strict */ true);
12001201
}
12011202
}
1203+
1204+
#ifndef ZEND_VERIFY_FUNC_INFO
1205+
# define ZEND_VERIFY_FUNC_INFO 0
1206+
#endif
1207+
1208+
static void zend_verify_internal_func_info(zend_function *fn, zval *retval) {
1209+
#if ZEND_VERIFY_FUNC_INFO
1210+
zend_string *name = fn->common.function_name;
1211+
uint32_t type_mask = zend_get_internal_func_info(fn, NULL, NULL);
1212+
if (!type_mask) {
1213+
return;
1214+
}
1215+
1216+
/* Always check refcount of arrays, as immutable arrays are RCN. */
1217+
if (Z_REFCOUNTED_P(retval) || Z_TYPE_P(retval) == IS_ARRAY) {
1218+
if (!(type_mask & MAY_BE_RC1)) {
1219+
zend_error_noreturn(E_CORE_ERROR, "%s() missing rc1", ZSTR_VAL(name));
1220+
}
1221+
if (Z_REFCOUNT_P(retval) > 1 && !(type_mask & MAY_BE_RCN)) {
1222+
zend_error_noreturn(E_CORE_ERROR, "%s() missing rcn", ZSTR_VAL(name));
1223+
}
1224+
}
1225+
1226+
uint32_t type = 1u << Z_TYPE_P(retval);
1227+
if (!(type_mask & type)) {
1228+
zend_error_noreturn(E_CORE_ERROR, "%s() missing type %s",
1229+
ZSTR_VAL(name), zend_get_type_by_const(Z_TYPE_P(retval)));
1230+
}
1231+
1232+
if (Z_TYPE_P(retval) == IS_ARRAY) {
1233+
HashTable *ht = Z_ARRVAL_P(retval);
1234+
uint32_t num_checked = 0;
1235+
zend_string *str;
1236+
zval *val;
1237+
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
1238+
if (str) {
1239+
if (!(type_mask & MAY_BE_ARRAY_KEY_STRING)) {
1240+
zend_error_noreturn(E_CORE_ERROR,
1241+
"%s() missing array_key_string", ZSTR_VAL(name));
1242+
}
1243+
} else {
1244+
if (!(type_mask & MAY_BE_ARRAY_KEY_LONG)) {
1245+
zend_error_noreturn(E_CORE_ERROR,
1246+
"%s() missing array_key_long", ZSTR_VAL(name));
1247+
}
1248+
}
1249+
1250+
uint32_t array_type = 1u << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
1251+
if (!(type_mask & array_type)) {
1252+
zend_error_noreturn(E_CORE_ERROR,
1253+
"%s() missing array element type %s",
1254+
ZSTR_VAL(name), zend_get_type_by_const(Z_TYPE_P(retval)));
1255+
}
1256+
1257+
/* Don't check all elements of large arrays. */
1258+
if (++num_checked > 16) {
1259+
break;
1260+
}
1261+
} ZEND_HASH_FOREACH_END();
1262+
}
1263+
#endif
1264+
}
12021265
#endif
12031266

12041267
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data)

Zend/zend_type_info.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
#define MAY_BE_CLASS (1<<24)
6868
#define MAY_BE_INDIRECT (1<<25)
6969

70+
#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */
71+
#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */
72+
7073

7174
#define MAY_BE_ANY_ARRAY \
7275
(MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)

Zend/zend_vm_def.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3905,6 +3905,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
39053905
zend_verify_internal_return_type(call->func, ret));
39063906
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
39073907
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
3908+
zend_verify_internal_func_info(call->func, ret);
39083909
}
39093910
#endif
39103911

@@ -4023,6 +4024,7 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER))
40234024
zend_verify_internal_return_type(call->func, ret));
40244025
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
40254026
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
4027+
zend_verify_internal_func_info(call->func, ret);
40264028
}
40274029
#endif
40284030

@@ -4132,6 +4134,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
41324134
zend_verify_internal_return_type(call->func, ret));
41334135
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
41344136
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
4137+
zend_verify_internal_func_info(call->func, ret);
41354138
}
41364139
#endif
41374140

@@ -8639,6 +8642,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER))
86398642
zend_verify_internal_return_type(call->func, ret));
86408643
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
86418644
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
8645+
zend_verify_internal_func_info(call->func, ret);
86428646
}
86438647
#endif
86448648

Zend/zend_vm_execute.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
12431243
zend_verify_internal_return_type(call->func, ret));
12441244
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
12451245
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
1246+
zend_verify_internal_func_info(call->func, ret);
12461247
}
12471248
#endif
12481249

@@ -1304,6 +1305,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
13041305
zend_verify_internal_return_type(call->func, ret));
13051306
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
13061307
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
1308+
zend_verify_internal_func_info(call->func, ret);
13071309
}
13081310
#endif
13091311

@@ -1469,6 +1471,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
14691471
zend_verify_internal_return_type(call->func, ret));
14701472
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
14711473
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
1474+
zend_verify_internal_func_info(call->func, ret);
14721475
}
14731476
#endif
14741477

@@ -1563,6 +1566,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
15631566
zend_verify_internal_return_type(call->func, ret));
15641567
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
15651568
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
1569+
zend_verify_internal_func_info(call->func, ret);
15661570
}
15671571
#endif
15681572

@@ -1658,6 +1662,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_
16581662
zend_verify_internal_return_type(call->func, ret));
16591663
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
16601664
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
1665+
zend_verify_internal_func_info(call->func, ret);
16611666
}
16621667
#endif
16631668

@@ -1767,6 +1772,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
17671772
zend_verify_internal_return_type(call->func, ret));
17681773
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
17691774
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
1775+
zend_verify_internal_func_info(call->func, ret);
17701776
}
17711777
#endif
17721778

@@ -1875,6 +1881,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
18751881
zend_verify_internal_return_type(call->func, ret));
18761882
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
18771883
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
1884+
zend_verify_internal_func_info(call->func, ret);
18781885
}
18791886
#endif
18801887

@@ -1983,6 +1990,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS
19831990
zend_verify_internal_return_type(call->func, ret));
19841991
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
19851992
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
1993+
zend_verify_internal_func_info(call->func, ret);
19861994
}
19871995
#endif
19881996

@@ -3338,6 +3346,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
33383346
zend_verify_internal_return_type(call->func, ret));
33393347
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
33403348
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
3349+
zend_verify_internal_func_info(call->func, ret);
33413350
}
33423351
#endif
33433352

@@ -3474,6 +3483,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_
34743483
zend_verify_internal_return_type(call->func, ret));
34753484
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
34763485
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
3486+
zend_verify_internal_func_info(call->func, ret);
34773487
}
34783488
#endif
34793489

azure-pipelines.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,4 @@ jobs:
116116
configurationName: VARIATION_DEBUG_ZTS
117117
configurationParameters: >-
118118
--enable-debug --enable-zts
119-
CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0"
119+
CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1"

0 commit comments

Comments
 (0)