1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend license,     |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "zend_type_info.h"
18 #include "zend_compile.h"
19 
20 #define VM_TRACE_START()
21 #define VM_TRACE_END()
22 #define VM_TRACE(op) zend_verify_inference_use(execute_data, OPLINE); \
23 	{ \
24 		zend_execute_data *__current_ex = NULL; \
25 		const zend_op *__current_op = NULL; \
26 		if (OPLINE->opcode != ZEND_GENERATOR_RETURN) { \
27 			__current_ex = execute_data; __current_op = OPLINE; \
28 		}
29 #define VM_TRACE_OP_END(op) \
30 		if (__current_ex && __current_op) { \
31 			zend_verify_inference_def(__current_ex, __current_op); \
32 		} \
33 	}
34 
35 #define ZEND_VERIFY_TYPE_INFERENCE_ERROR(msg, ...) \
36 	do { \
37 		fprintf(stderr, "Inference verification failed at %04d %s (" msg ")\n", (int)(opline - EX(func)->op_array.opcodes), operand, __VA_ARGS__); \
38 		_exit(139); \
39 	} while (0)
40 
zend_verify_type_inference(zval * value,uint32_t type_mask,uint8_t op_type,zend_execute_data * execute_data,const zend_op * opline,const char * operand)41 static void zend_verify_type_inference(zval *value, uint32_t type_mask, uint8_t op_type, zend_execute_data *execute_data, const zend_op *opline, const char *operand)
42 {
43 	if (type_mask == MAY_BE_CLASS) {
44 		return;
45 	}
46 
47 	if (Z_TYPE_P(value) == IS_INDIRECT) {
48 		if (!(type_mask & MAY_BE_INDIRECT)) {
49 			ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x mising MAY_BE_INDIRECT", type_mask);
50 		}
51 		value = Z_INDIRECT_P(value);
52 	}
53 
54 	/* Verifying RC inference is currently not possible because type information is based on the SSA
55 	 * built without ZEND_SSA_RC_INFERENCE, which is missing various definitions for RC-modifying
56 	 * operations. Support could be added by repeating SSA-construction and type inference with the
57 	 * given flag. */
58 	// if (Z_REFCOUNTED_P(value)) {
59 	// 	if (Z_REFCOUNT_P(value) == 1 && !(type_mask & MAY_BE_RC1)) {
60 	// 		ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing MAY_BE_RC1", type_mask);
61 	// 	}
62 	// 	if (Z_REFCOUNT_P(value) > 1 && !(type_mask & MAY_BE_RCN)) {
63 	// 		ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing MAY_BE_RCN", type_mask);
64 	// 	}
65 	// }
66 
67 	if (Z_TYPE_P(value) == IS_REFERENCE) {
68 		if (!(type_mask & MAY_BE_REF)) {
69 			ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing MAY_BE_REF", type_mask);
70 		}
71 		value = Z_REFVAL_P(value);
72 	}
73 
74 	if (!(type_mask & (1u << Z_TYPE_P(value)))) {
75 		if (Z_TYPE_P(value) == IS_UNUSED && op_type == IS_VAR && (type_mask & MAY_BE_NULL)) {
76 			/* FETCH_OBJ_* for typed property may return IS_UNDEF. This is an exception. */
77 		} else {
78 			ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing type %d", type_mask, Z_TYPE_P(value));
79 		}
80 	}
81 
82 	if (Z_TYPE_P(value) == IS_ARRAY) {
83 		HashTable *ht = Z_ARRVAL_P(value);
84 		uint32_t num_checked = 0;
85 		zend_string *str;
86 		zval *val;
87 		if (HT_IS_INITIALIZED(ht)) {
88 			if (HT_IS_PACKED(ht) && !MAY_BE_PACKED(type_mask)) {
89 				ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing MAY_BE_ARRAY_PACKED", type_mask);
90 			}
91 			if (!HT_IS_PACKED(ht) && !MAY_BE_HASH(type_mask)) {
92 				ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing MAY_BE_ARRAY_HASH", type_mask);
93 			}
94 		} else {
95 			if (!(type_mask & MAY_BE_ARRAY_EMPTY)) {
96 				ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing MAY_BE_ARRAY_EMPTY", type_mask);
97 			}
98 		}
99 		ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
100 			if (str) {
101 				if (!(type_mask & MAY_BE_ARRAY_KEY_STRING)) {
102 					ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing MAY_BE_ARRAY_KEY_STRING", type_mask);
103 					break;
104 				}
105 			} else {
106 				if (!(type_mask & MAY_BE_ARRAY_KEY_LONG)) {
107 					ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing MAY_BE_ARRAY_KEY_LONG", type_mask);
108 					break;
109 				}
110 			}
111 
112 			uint32_t array_type = 1u << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
113 			if (!(type_mask & array_type)) {
114 				ZEND_VERIFY_TYPE_INFERENCE_ERROR("mask 0x%x missing array type %d", type_mask, Z_TYPE_P(val));
115 				break;
116 			}
117 
118 			/* Don't check all elements of large arrays. */
119 			if (++num_checked > 16) {
120 				break;
121 			}
122 		} ZEND_HASH_FOREACH_END();
123 	}
124 }
125 
126 /* Clang reports false positive unused warnings. */
127 #ifdef __clang__
128 __attribute__((unused))
129 #endif
zend_verify_inference_use(zend_execute_data * execute_data,const zend_op * opline)130 static void zend_verify_inference_use(zend_execute_data *execute_data, const zend_op *opline)
131 {
132 	if (opline->op1_use_type
133 	 && (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV))
134 	 && opline->opcode != ZEND_ROPE_ADD
135 	 && opline->opcode != ZEND_ROPE_END) {
136 		zend_verify_type_inference(EX_VAR(opline->op1.var), opline->op1_use_type, opline->op1_type, execute_data, opline, "op1_use");
137 	}
138 	if (opline->op2_use_type
139 	 && (opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))) {
140 		zend_verify_type_inference(EX_VAR(opline->op2.var), opline->op2_use_type, opline->op2_type, execute_data, opline, "op2_use");
141 	}
142 	if (opline->result_use_type
143 	 && (opline->result_type & (IS_TMP_VAR|IS_VAR|IS_CV))) {
144 		zend_verify_type_inference(EX_VAR(opline->result.var), opline->result_use_type, opline->result_type, execute_data, opline, "result_use");
145 	}
146 }
147 
148 /* Clang reports false positive unused warnings. */
149 #ifdef __clang__
150 __attribute__((unused))
151 #endif
zend_verify_inference_def(zend_execute_data * execute_data,const zend_op * opline)152 static void zend_verify_inference_def(zend_execute_data *execute_data, const zend_op *opline)
153 {
154 	if (EG(exception)) {
155 		return;
156 	}
157 	if (opline->op1_def_type
158 	 && (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV))
159 	 // array is actually changed by the the following instruction(s)
160 	 && opline->opcode != ZEND_FETCH_DIM_W
161 	 && opline->opcode != ZEND_FETCH_DIM_RW
162 	 && opline->opcode != ZEND_FETCH_DIM_FUNC_ARG
163 	 && opline->opcode != ZEND_FETCH_LIST_W) {
164 		zend_verify_type_inference(EX_VAR(opline->op1.var), opline->op1_def_type, opline->op1_type, execute_data, opline, "op1_def");
165 	}
166 	if (opline->op2_def_type
167 	 && (opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
168 	 /* ZEND_FE_FETCH_R[W] does not define a result in the last iteration. */
169 	 && opline->opcode != ZEND_FE_FETCH_R
170 	 && opline->opcode != ZEND_FE_FETCH_RW) {
171 		zend_verify_type_inference(EX_VAR(opline->op2.var), opline->op2_def_type, opline->op2_type, execute_data, opline, "op2_def");
172 	}
173 	if (opline->result_def_type
174 	 && (opline->result_type & (IS_TMP_VAR|IS_VAR|IS_CV))
175 	 && opline->opcode != ZEND_ROPE_INIT
176 	 && opline->opcode != ZEND_ROPE_ADD
177 	 /* Some jump opcode handlers don't set result when it's never read. */
178 	 && opline->opcode != ZEND_JMP_SET
179 	 && opline->opcode != ZEND_JMP_NULL
180 	 && opline->opcode != ZEND_COALESCE
181 	 && opline->opcode != ZEND_ASSERT_CHECK
182 	 /* Smart branches may not declare result. */
183 	 && !zend_is_smart_branch(opline)
184 	 /* User calls only initialize result when returning from the called function. */
185 	 && opline->opcode != ZEND_DO_FCALL
186 	 && opline->opcode != ZEND_DO_UCALL
187 	 && opline->opcode != ZEND_DO_FCALL_BY_NAME
188 	 /* ZEND_FE_FETCH_R[W] does not define a result in the last iteration. */
189 	 && opline->opcode != ZEND_FE_FETCH_R
190 	 && opline->opcode != ZEND_FE_FETCH_RW) {
191 		zend_verify_type_inference(EX_VAR(opline->result.var), opline->result_def_type, opline->result_type, execute_data, opline, "result_def");
192 
193 		/* Verify return value in the context of caller. */
194 		if ((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_RETURN_BY_REF)
195 		 && execute_data->prev_execute_data
196 		 && execute_data->prev_execute_data->func
197 		 && ZEND_USER_CODE(execute_data->prev_execute_data->func->type)) {
198 			zend_execute_data *prev_execute_data = execute_data->prev_execute_data;
199 			const zend_op *opline = execute_data->prev_execute_data->opline;
200 			zend_verify_type_inference(ZEND_CALL_VAR(prev_execute_data, opline->result.var), opline->result_def_type, opline->result_type, prev_execute_data, opline, "result_def");
201 		}
202 	}
203 }
204