1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine, SCCP - Sparse Conditional Constant Propagation |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Nikita Popov <nikic@php.net> |
16 | Dmitry Stogov <dmitry@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "php.h"
21 #include "zend_type_info.h"
22 #include "ZendAccelerator.h"
23 #include "Optimizer/zend_optimizer_internal.h"
24 #include "Optimizer/zend_call_graph.h"
25 #include "Optimizer/zend_inference.h"
26 #include "Optimizer/scdf.h"
27 #include "Optimizer/zend_dump.h"
28 #include "ext/standard/php_string.h"
29
30 /* This implements sparse conditional constant propagation (SCCP) based on the SCDF framework. The
31 * used value lattice is defined as follows:
32 *
33 * BOT < {constant values} < TOP
34 *
35 * TOP indicates an underdefined value, i.e. that we do not yet know the value of variable.
36 * BOT indicates an overdefined value, i.e. that we know the variable to be non-constant.
37 *
38 * All variables are optimistically initialized to TOP, apart from the implicit variables defined
39 * at the start of the first block. Note that variables that MAY_BE_REF are *not* initialized to
40 * BOT. We rely on the fact that any operation resulting in a reference will produce a BOT anyway.
41 * This is better because such operations might never be reached due to the conditional nature of
42 * the algorithm.
43 *
44 * The meet operation for phi functions is defined as follows:
45 * BOT + any = BOT
46 * TOP + any = any
47 * C_i + C_i = C_i (i.e. two equal constants)
48 * C_i + C_j = BOT (i.e. two different constants)
49 *
50 * When evaluating instructions TOP and BOT are handled as follows:
51 * a) If any operand is BOT, the result is BOT. The main exception to this is op1 of ASSIGN, which
52 * is ignored. However, if the op1 MAY_BE_REF we do have to propagate the BOT.
53 * b) Otherwise, if the instruction can never be evaluated (either in general, or with the
54 * specific modifiers) the result is BOT.
55 * c) Otherwise, if any operand is TOP, the result is TOP.
56 * d) Otherwise (at this point all operands are known and constant), if we can compute the result
57 * for these specific constants (without throwing notices or similar) then that is the result.
58 * e) Otherwise the result is BOT.
59 *
60 * It is sometimes possible to determine a result even if one argument is TOP / BOT, e.g. for things
61 * like BOT*0. Right now we don't bother with this -- the only thing that is done is evaluating
62 * TYPE_CHECKS based on the type information.
63 *
64 * Feasible successors for conditional branches are determined as follows:
65 * a) If we don't support the branch type or branch on BOT, all successors are feasible.
66 * b) Otherwise, if we branch on TOP none of the successors are feasible.
67 * c) Otherwise (we branch on a constant), the feasible successors are marked based on the constant
68 * (usually only one successor will be feasible).
69 *
70 * The original SCCP algorithm is extended with ability to propagate constant array
71 * elements and object properties. The extension is based on a variation of Array
72 * SSA form and its application to Spare Constant Propagation, described at
73 * "Array SSA Form" by Vivek Sarkar, Kathleen Knobe and Stephen Fink in chapter
74 * 16 of the SSA book.
75 */
76
77 #define SCP_DEBUG 0
78
79 typedef struct _sccp_ctx {
80 scdf_ctx scdf;
81 zend_call_info **call_map;
82 zval *values;
83 zval top;
84 zval bot;
85 } sccp_ctx;
86
87 #define TOP ((zend_uchar)-1)
88 #define BOT ((zend_uchar)-2)
89 #define PARTIAL_ARRAY ((zend_uchar)-3)
90 #define PARTIAL_OBJECT ((zend_uchar)-4)
91 #define IS_TOP(zv) (Z_TYPE_P(zv) == TOP)
92 #define IS_BOT(zv) (Z_TYPE_P(zv) == BOT)
93 #define IS_PARTIAL_ARRAY(zv) (Z_TYPE_P(zv) == PARTIAL_ARRAY)
94 #define IS_PARTIAL_OBJECT(zv) (Z_TYPE_P(zv) == PARTIAL_OBJECT)
95
96 #define MAKE_PARTIAL_ARRAY(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_ARRAY | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
97 #define MAKE_PARTIAL_OBJECT(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
98
99 #define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP)
100 #define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT)
101
scp_dump_value(zval * zv)102 static void scp_dump_value(zval *zv) {
103 if (IS_TOP(zv)) {
104 fprintf(stderr, " top");
105 } else if (IS_BOT(zv)) {
106 fprintf(stderr, " bot");
107 } else if (Z_TYPE_P(zv) == IS_ARRAY || IS_PARTIAL_ARRAY(zv)) {
108 fprintf(stderr, " %s[", IS_PARTIAL_ARRAY(zv) ? "partial " : "");
109 zend_dump_ht(Z_ARRVAL_P(zv));
110 fprintf(stderr, "]");
111 } else if (IS_PARTIAL_OBJECT(zv)) {
112 fprintf(stderr, " {");
113 zend_dump_ht(Z_ARRVAL_P(zv));
114 fprintf(stderr, "}");
115 } else {
116 zend_dump_const(zv);
117 }
118 }
119
empty_partial_array(zval * zv)120 static void empty_partial_array(zval *zv)
121 {
122 MAKE_PARTIAL_ARRAY(zv);
123 Z_ARR_P(zv) = zend_new_array(8);
124 }
125
dup_partial_array(zval * dst,zval * src)126 static void dup_partial_array(zval *dst, zval *src)
127 {
128 MAKE_PARTIAL_ARRAY(dst);
129 Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
130 }
131
empty_partial_object(zval * zv)132 static void empty_partial_object(zval *zv)
133 {
134 MAKE_PARTIAL_OBJECT(zv);
135 Z_ARR_P(zv) = zend_new_array(8);
136 }
137
dup_partial_object(zval * dst,zval * src)138 static void dup_partial_object(zval *dst, zval *src)
139 {
140 MAKE_PARTIAL_OBJECT(dst);
141 Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
142 }
143
value_known(zval * zv)144 static inline zend_bool value_known(zval *zv) {
145 return !IS_TOP(zv) && !IS_BOT(zv);
146 }
147
148 /* Sets new value for variable and ensures that it is lower or equal
149 * the previous one in the constant propagation lattice. */
set_value(scdf_ctx * scdf,sccp_ctx * ctx,int var,zval * new)150 static void set_value(scdf_ctx *scdf, sccp_ctx *ctx, int var, zval *new) {
151 zval *value = &ctx->values[var];
152 if (IS_BOT(value) || IS_TOP(new)) {
153 return;
154 }
155
156 #if SCP_DEBUG
157 fprintf(stderr, "Lowering #%d.", var);
158 zend_dump_var(scdf->op_array, IS_CV, scdf->ssa->vars[var].var);
159 fprintf(stderr, " from");
160 scp_dump_value(value);
161 fprintf(stderr, " to");
162 scp_dump_value(new);
163 fprintf(stderr, "\n");
164 #endif
165
166 if (IS_TOP(value) || IS_BOT(new)) {
167 zval_ptr_dtor_nogc(value);
168 ZVAL_COPY(value, new);
169 scdf_add_to_worklist(scdf, var);
170 return;
171 }
172
173 /* Always replace PARTIAL_(ARRAY|OBJECT), as new maybe changed by join_partial_(arrays|object) */
174 if (IS_PARTIAL_ARRAY(new) || IS_PARTIAL_OBJECT(new)) {
175 if (Z_TYPE_P(value) != Z_TYPE_P(new)
176 || zend_hash_num_elements(Z_ARR_P(new)) != zend_hash_num_elements(Z_ARR_P(value))) {
177 zval_ptr_dtor_nogc(value);
178 ZVAL_COPY(value, new);
179 scdf_add_to_worklist(scdf, var);
180 }
181 return;
182 }
183
184 #if ZEND_DEBUG
185 ZEND_ASSERT(zend_is_identical(value, new));
186 #endif
187 }
188
get_op1_value(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op)189 static zval *get_op1_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
190 if (opline->op1_type == IS_CONST) {
191 return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op1.constant);
192 } else if (ssa_op->op1_use != -1) {
193 return &ctx->values[ssa_op->op1_use];
194 } else {
195 return NULL;
196 }
197 }
198
get_op2_value(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op)199 static zval *get_op2_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
200 if (opline->op2_type == IS_CONST) {
201 return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op2.constant);
202 } else if (ssa_op->op2_use != -1) {
203 return &ctx->values[ssa_op->op2_use];
204 } else {
205 return NULL;
206 }
207 }
208
can_replace_op1(const zend_op_array * op_array,zend_op * opline,zend_ssa_op * ssa_op)209 static zend_bool can_replace_op1(
210 const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
211 switch (opline->opcode) {
212 case ZEND_PRE_INC:
213 case ZEND_PRE_DEC:
214 case ZEND_PRE_INC_OBJ:
215 case ZEND_PRE_DEC_OBJ:
216 case ZEND_POST_INC:
217 case ZEND_POST_DEC:
218 case ZEND_POST_INC_OBJ:
219 case ZEND_POST_DEC_OBJ:
220 case ZEND_ASSIGN:
221 case ZEND_ASSIGN_REF:
222 case ZEND_ASSIGN_DIM:
223 case ZEND_ASSIGN_OBJ:
224 case ZEND_ASSIGN_OBJ_REF:
225 case ZEND_ASSIGN_OP:
226 case ZEND_ASSIGN_DIM_OP:
227 case ZEND_ASSIGN_OBJ_OP:
228 case ZEND_ASSIGN_STATIC_PROP_OP:
229 case ZEND_FETCH_DIM_W:
230 case ZEND_FETCH_DIM_RW:
231 case ZEND_FETCH_DIM_UNSET:
232 case ZEND_FETCH_DIM_FUNC_ARG:
233 case ZEND_FETCH_OBJ_W:
234 case ZEND_FETCH_OBJ_RW:
235 case ZEND_FETCH_OBJ_UNSET:
236 case ZEND_FETCH_OBJ_FUNC_ARG:
237 case ZEND_FETCH_LIST_W:
238 case ZEND_UNSET_DIM:
239 case ZEND_UNSET_OBJ:
240 case ZEND_SEND_REF:
241 case ZEND_SEND_VAR_EX:
242 case ZEND_SEND_FUNC_ARG:
243 case ZEND_SEND_UNPACK:
244 case ZEND_SEND_ARRAY:
245 case ZEND_SEND_USER:
246 case ZEND_FE_RESET_RW:
247 return 0;
248 /* Do not accept CONST */
249 case ZEND_ROPE_ADD:
250 case ZEND_ROPE_END:
251 case ZEND_BIND_STATIC:
252 case ZEND_BIND_GLOBAL:
253 case ZEND_MAKE_REF:
254 case ZEND_UNSET_CV:
255 case ZEND_ISSET_ISEMPTY_CV:
256 return 0;
257 case ZEND_INIT_ARRAY:
258 case ZEND_ADD_ARRAY_ELEMENT:
259 return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF);
260 case ZEND_YIELD:
261 return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE);
262 case ZEND_VERIFY_RETURN_TYPE:
263 // TODO: This would require a non-local change ???
264 return 0;
265 case ZEND_OP_DATA:
266 return (opline - 1)->opcode != ZEND_ASSIGN_OBJ_REF &&
267 (opline - 1)->opcode != ZEND_ASSIGN_STATIC_PROP_REF;
268 default:
269 if (ssa_op->op1_def != -1) {
270 ZEND_ASSERT(0);
271 return 0;
272 }
273 }
274
275 return 1;
276 }
277
can_replace_op2(const zend_op_array * op_array,zend_op * opline,zend_ssa_op * ssa_op)278 static zend_bool can_replace_op2(
279 const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
280 switch (opline->opcode) {
281 /* Do not accept CONST */
282 case ZEND_DECLARE_CLASS_DELAYED:
283 case ZEND_BIND_LEXICAL:
284 case ZEND_FE_FETCH_R:
285 case ZEND_FE_FETCH_RW:
286 return 0;
287 }
288 return 1;
289 }
290
try_replace_op1(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op,int var,zval * value)291 static zend_bool try_replace_op1(
292 sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
293 if (ssa_op->op1_use == var && can_replace_op1(ctx->scdf.op_array, opline, ssa_op)) {
294 zval zv;
295 ZVAL_COPY(&zv, value);
296 if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) {
297 return 1;
298 } else {
299 // TODO: check the following special cases ???
300 switch (opline->opcode) {
301 case ZEND_CASE:
302 opline->opcode = ZEND_IS_EQUAL;
303 /* break missing intentionally */
304 case ZEND_FETCH_LIST_R:
305 case ZEND_SWITCH_STRING:
306 case ZEND_SWITCH_LONG:
307 if (Z_TYPE(zv) == IS_STRING) {
308 zend_string_hash_val(Z_STR(zv));
309 }
310 opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv);
311 opline->op1_type = IS_CONST;
312 return 1;
313 case ZEND_INSTANCEOF:
314 zval_ptr_dtor_nogc(&zv);
315 ZVAL_FALSE(&zv);
316 opline->opcode = ZEND_QM_ASSIGN;
317 opline->op1_type = IS_CONST;
318 opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv);
319 opline->op2_type = IS_UNUSED;
320 if (ssa_op->op2_use >= 0) {
321 ZEND_ASSERT(ssa_op->op2_def == -1);
322 zend_ssa_unlink_use_chain(ctx->scdf.ssa, ssa_op - ctx->scdf.ssa->ops, ssa_op->op2_use);
323 ssa_op->op2_use = -1;
324 ssa_op->op2_use_chain = -1;
325 }
326 return 1;
327 default:
328 break;
329 }
330 zval_ptr_dtor_nogc(&zv);
331 }
332 }
333 return 0;
334 }
335
try_replace_op2(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op,int var,zval * value)336 static zend_bool try_replace_op2(
337 sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
338 if (ssa_op->op2_use == var && can_replace_op2(ctx->scdf.op_array, opline, ssa_op)) {
339 zval zv;
340 ZVAL_COPY(&zv, value);
341 if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline, &zv)) {
342 return 1;
343 } else {
344 switch (opline->opcode) {
345 case ZEND_FETCH_CLASS:
346 if (Z_TYPE(zv) == IS_STRING) {
347 ZEND_ASSERT((opline + 1)->opcode == ZEND_INSTANCEOF);
348 ZEND_ASSERT(ssa_op->result_def == (ssa_op + 1)->op2_use);
349 if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline + 1, &zv)) {
350 zend_ssa_op *next_op = ssa_op + 1;
351 zend_ssa_unlink_use_chain(ctx->scdf.ssa, next_op - ctx->scdf.ssa->ops, next_op->op2_use);
352 next_op->op2_use = -1;
353 next_op->op2_use_chain = -1;
354 zend_ssa_remove_result_def(ctx->scdf.ssa, ssa_op);
355 MAKE_NOP(opline);
356 return 1;
357 }
358 }
359 default:
360 break;
361 }
362 zval_ptr_dtor_nogc(&zv);
363 }
364 }
365 return 0;
366 }
367
ct_eval_binary_op(zval * result,zend_uchar binop,zval * op1,zval * op2)368 static inline int ct_eval_binary_op(zval *result, zend_uchar binop, zval *op1, zval *op2) {
369 /* TODO: We could implement support for evaluation of + on partial arrays. */
370 if (IS_PARTIAL_ARRAY(op1) || IS_PARTIAL_ARRAY(op2)) {
371 return FAILURE;
372 }
373
374 return zend_optimizer_eval_binary_op(result, binop, op1, op2);
375 }
376
ct_eval_bool_cast(zval * result,zval * op)377 static inline int ct_eval_bool_cast(zval *result, zval *op) {
378 if (IS_PARTIAL_ARRAY(op)) {
379 if (zend_hash_num_elements(Z_ARRVAL_P(op)) == 0) {
380 /* An empty partial array may be non-empty at runtime, we don't know whether the
381 * result will be true or false. */
382 return FAILURE;
383 }
384
385 ZVAL_TRUE(result);
386 return SUCCESS;
387 }
388
389 ZVAL_BOOL(result, zend_is_true(op));
390 return SUCCESS;
391 }
392
zval_to_string_offset(zend_long * result,zval * op)393 static inline int zval_to_string_offset(zend_long *result, zval *op) {
394 switch (Z_TYPE_P(op)) {
395 case IS_LONG:
396 *result = Z_LVAL_P(op);
397 return SUCCESS;
398 case IS_STRING:
399 if (IS_LONG == is_numeric_string(
400 Z_STRVAL_P(op), Z_STRLEN_P(op), result, NULL, 0)) {
401 return SUCCESS;
402 }
403 return FAILURE;
404 default:
405 return FAILURE;
406 }
407 }
408
fetch_array_elem(zval ** result,zval * op1,zval * op2)409 static inline int fetch_array_elem(zval **result, zval *op1, zval *op2) {
410 switch (Z_TYPE_P(op2)) {
411 case IS_NULL:
412 *result = zend_hash_find(Z_ARR_P(op1), ZSTR_EMPTY_ALLOC());
413 return SUCCESS;
414 case IS_FALSE:
415 *result = zend_hash_index_find(Z_ARR_P(op1), 0);
416 return SUCCESS;
417 case IS_TRUE:
418 *result = zend_hash_index_find(Z_ARR_P(op1), 1);
419 return SUCCESS;
420 case IS_LONG:
421 *result = zend_hash_index_find(Z_ARR_P(op1), Z_LVAL_P(op2));
422 return SUCCESS;
423 case IS_DOUBLE:
424 *result = zend_hash_index_find(Z_ARR_P(op1), zend_dval_to_lval(Z_DVAL_P(op2)));
425 return SUCCESS;
426 case IS_STRING:
427 *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
428 return SUCCESS;
429 default:
430 return FAILURE;
431 }
432 }
433
ct_eval_fetch_dim(zval * result,zval * op1,zval * op2,int support_strings)434 static inline int ct_eval_fetch_dim(zval *result, zval *op1, zval *op2, int support_strings) {
435 if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
436 zval *value;
437 if (fetch_array_elem(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
438 ZVAL_COPY(result, value);
439 return SUCCESS;
440 }
441 } else if (support_strings && Z_TYPE_P(op1) == IS_STRING) {
442 zend_long index;
443 if (zval_to_string_offset(&index, op2) == FAILURE) {
444 return FAILURE;
445 }
446 if (index >= 0 && index < Z_STRLEN_P(op1)) {
447 ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0));
448 return SUCCESS;
449 }
450 }
451 return FAILURE;
452 }
453
454 /* op1 may be NULL here to indicate an unset value */
ct_eval_isset_isempty(zval * result,uint32_t extended_value,zval * op1)455 static inline int ct_eval_isset_isempty(zval *result, uint32_t extended_value, zval *op1) {
456 zval zv;
457 if (!(extended_value & ZEND_ISEMPTY)) {
458 ZVAL_BOOL(result, op1 && Z_TYPE_P(op1) != IS_NULL);
459 return SUCCESS;
460 } else if (!op1) {
461 ZVAL_TRUE(result);
462 return SUCCESS;
463 } else if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
464 ZVAL_BOOL(result, Z_TYPE(zv) == IS_FALSE);
465 return SUCCESS;
466 } else {
467 return FAILURE;
468 }
469 }
470
ct_eval_isset_dim(zval * result,uint32_t extended_value,zval * op1,zval * op2)471 static inline int ct_eval_isset_dim(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
472 if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
473 zval *value;
474 if (fetch_array_elem(&value, op1, op2) == FAILURE) {
475 return FAILURE;
476 }
477 if (IS_PARTIAL_ARRAY(op1) && (!value || IS_BOT(value))) {
478 return FAILURE;
479 }
480 return ct_eval_isset_isempty(result, extended_value, value);
481 } else if (Z_TYPE_P(op1) == IS_STRING) {
482 // TODO
483 return FAILURE;
484 } else {
485 ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY));
486 return SUCCESS;
487 }
488 }
489
ct_eval_del_array_elem(zval * result,zval * key)490 static inline int ct_eval_del_array_elem(zval *result, zval *key) {
491 ZEND_ASSERT(IS_PARTIAL_ARRAY(result));
492
493 switch (Z_TYPE_P(key)) {
494 case IS_NULL:
495 zend_hash_del(Z_ARR_P(result), ZSTR_EMPTY_ALLOC());
496 break;
497 case IS_FALSE:
498 zend_hash_index_del(Z_ARR_P(result), 0);
499 break;
500 case IS_TRUE:
501 zend_hash_index_del(Z_ARR_P(result), 1);
502 break;
503 case IS_LONG:
504 zend_hash_index_del(Z_ARR_P(result), Z_LVAL_P(key));
505 break;
506 case IS_DOUBLE:
507 zend_hash_index_del(Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)));
508 break;
509 case IS_STRING:
510 zend_symtable_del(Z_ARR_P(result), Z_STR_P(key));
511 break;
512 default:
513 return FAILURE;
514 }
515
516 return SUCCESS;
517 }
518
ct_eval_add_array_elem(zval * result,zval * value,zval * key)519 static inline int ct_eval_add_array_elem(zval *result, zval *value, zval *key) {
520 if (!key) {
521 SEPARATE_ARRAY(result);
522 if ((value = zend_hash_next_index_insert(Z_ARR_P(result), value))) {
523 Z_TRY_ADDREF_P(value);
524 return SUCCESS;
525 }
526 return FAILURE;
527 }
528
529 switch (Z_TYPE_P(key)) {
530 case IS_NULL:
531 SEPARATE_ARRAY(result);
532 value = zend_hash_update(Z_ARR_P(result), ZSTR_EMPTY_ALLOC(), value);
533 break;
534 case IS_FALSE:
535 SEPARATE_ARRAY(result);
536 value = zend_hash_index_update(Z_ARR_P(result), 0, value);
537 break;
538 case IS_TRUE:
539 SEPARATE_ARRAY(result);
540 value = zend_hash_index_update(Z_ARR_P(result), 1, value);
541 break;
542 case IS_LONG:
543 SEPARATE_ARRAY(result);
544 value = zend_hash_index_update(Z_ARR_P(result), Z_LVAL_P(key), value);
545 break;
546 case IS_DOUBLE:
547 SEPARATE_ARRAY(result);
548 value = zend_hash_index_update(
549 Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)), value);
550 break;
551 case IS_STRING:
552 SEPARATE_ARRAY(result);
553 value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
554 break;
555 default:
556 return FAILURE;
557 }
558
559 Z_TRY_ADDREF_P(value);
560 return SUCCESS;
561 }
562
ct_eval_add_array_unpack(zval * result,zval * array)563 static inline int ct_eval_add_array_unpack(zval *result, zval *array) {
564 zend_string *key;
565 zval *value;
566 if (Z_TYPE_P(array) != IS_ARRAY) {
567 return FAILURE;
568 }
569
570 SEPARATE_ARRAY(result);
571 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(array), key, value) {
572 if (key) {
573 return FAILURE;
574 }
575 value = zend_hash_next_index_insert(Z_ARR_P(result), value);
576 if (!value) {
577 return FAILURE;
578 }
579 Z_TRY_ADDREF_P(value);
580 } ZEND_HASH_FOREACH_END();
581 return SUCCESS;
582 }
583
ct_eval_assign_dim(zval * result,zval * value,zval * key)584 static inline int ct_eval_assign_dim(zval *result, zval *value, zval *key) {
585 switch (Z_TYPE_P(result)) {
586 case IS_NULL:
587 case IS_FALSE:
588 array_init(result);
589 /* break missing intentionally */
590 case IS_ARRAY:
591 case PARTIAL_ARRAY:
592 return ct_eval_add_array_elem(result, value, key);
593 case IS_STRING:
594 // TODO Before enabling this case, make sure ARRAY_DIM result op is correct
595 #if 0
596 zend_long index;
597 zend_string *new_str, *value_str;
598 if (!key || Z_TYPE_P(value) == IS_ARRAY
599 || zval_to_string_offset(&index, key) == FAILURE || index < 0) {
600 return FAILURE;
601 }
602
603 if (index >= Z_STRLEN_P(result)) {
604 new_str = zend_string_alloc(index + 1, 0);
605 memcpy(ZSTR_VAL(new_str), Z_STRVAL_P(result), Z_STRLEN_P(result));
606 memset(ZSTR_VAL(new_str) + Z_STRLEN_P(result), ' ', index - Z_STRLEN_P(result));
607 ZSTR_VAL(new_str)[index + 1] = 0;
608 } else {
609 new_str = zend_string_init(Z_STRVAL_P(result), Z_STRLEN_P(result), 0);
610 }
611
612 value_str = zval_get_string(value);
613 ZVAL_STR(result, new_str);
614 Z_STRVAL_P(result)[index] = ZSTR_VAL(value_str)[0];
615 zend_string_release_ex(value_str, 0);
616 #endif
617 return FAILURE;
618 default:
619 return FAILURE;
620 }
621 }
622
fetch_obj_prop(zval ** result,zval * op1,zval * op2)623 static inline int fetch_obj_prop(zval **result, zval *op1, zval *op2) {
624 switch (Z_TYPE_P(op2)) {
625 case IS_STRING:
626 *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
627 return SUCCESS;
628 default:
629 return FAILURE;
630 }
631 }
632
ct_eval_fetch_obj(zval * result,zval * op1,zval * op2)633 static inline int ct_eval_fetch_obj(zval *result, zval *op1, zval *op2) {
634 if (IS_PARTIAL_OBJECT(op1)) {
635 zval *value;
636 if (fetch_obj_prop(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
637 ZVAL_COPY(result, value);
638 return SUCCESS;
639 }
640 }
641 return FAILURE;
642 }
643
ct_eval_isset_obj(zval * result,uint32_t extended_value,zval * op1,zval * op2)644 static inline int ct_eval_isset_obj(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
645 if (IS_PARTIAL_OBJECT(op1)) {
646 zval *value;
647 if (fetch_obj_prop(&value, op1, op2) == FAILURE) {
648 return FAILURE;
649 }
650 if (!value || IS_BOT(value)) {
651 return FAILURE;
652 }
653 return ct_eval_isset_isempty(result, extended_value, value);
654 } else {
655 ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY));
656 return SUCCESS;
657 }
658 }
659
ct_eval_del_obj_prop(zval * result,zval * key)660 static inline int ct_eval_del_obj_prop(zval *result, zval *key) {
661 ZEND_ASSERT(IS_PARTIAL_OBJECT(result));
662
663 switch (Z_TYPE_P(key)) {
664 case IS_STRING:
665 zend_symtable_del(Z_ARR_P(result), Z_STR_P(key));
666 break;
667 default:
668 return FAILURE;
669 }
670
671 return SUCCESS;
672 }
673
ct_eval_add_obj_prop(zval * result,zval * value,zval * key)674 static inline int ct_eval_add_obj_prop(zval *result, zval *value, zval *key) {
675 switch (Z_TYPE_P(key)) {
676 case IS_STRING:
677 value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
678 break;
679 default:
680 return FAILURE;
681 }
682
683 Z_TRY_ADDREF_P(value);
684 return SUCCESS;
685 }
686
ct_eval_assign_obj(zval * result,zval * value,zval * key)687 static inline int ct_eval_assign_obj(zval *result, zval *value, zval *key) {
688 switch (Z_TYPE_P(result)) {
689 case IS_NULL:
690 case IS_FALSE:
691 empty_partial_object(result);
692 /* break missing intentionally */
693 case PARTIAL_OBJECT:
694 return ct_eval_add_obj_prop(result, value, key);
695 default:
696 return FAILURE;
697 }
698 }
699
ct_eval_incdec(zval * result,zend_uchar opcode,zval * op1)700 static inline int ct_eval_incdec(zval *result, zend_uchar opcode, zval *op1) {
701 ZVAL_COPY(result, op1);
702 if (opcode == ZEND_PRE_INC
703 || opcode == ZEND_POST_INC
704 || opcode == ZEND_PRE_INC_OBJ
705 || opcode == ZEND_POST_INC_OBJ) {
706 increment_function(result);
707 } else {
708 decrement_function(result);
709 }
710 return SUCCESS;
711 }
712
ct_eval_type_check(zval * result,uint32_t type_mask,zval * op1)713 static inline void ct_eval_type_check(zval *result, uint32_t type_mask, zval *op1) {
714 uint32_t type = Z_TYPE_P(op1);
715 if (type == PARTIAL_ARRAY) {
716 type = IS_ARRAY;
717 } else if (type == PARTIAL_OBJECT) {
718 type = IS_OBJECT;
719 }
720 ZVAL_BOOL(result, (type_mask >> type) & 1);
721 }
722
ct_eval_in_array(zval * result,uint32_t extended_value,zval * op1,zval * op2)723 static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
724 HashTable *ht;
725 zend_bool res;
726
727 if (Z_TYPE_P(op2) != IS_ARRAY) {
728 return FAILURE;
729 }
730 ht = Z_ARRVAL_P(op2);
731 if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
732 res = zend_hash_exists(ht, Z_STR_P(op1));
733 } else if (extended_value) {
734 if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
735 res = zend_hash_index_exists(ht, Z_LVAL_P(op1));
736 } else {
737 res = 0;
738 }
739 } else if (Z_TYPE_P(op1) <= IS_FALSE) {
740 res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC());
741 } else {
742 zend_string *key;
743 zval key_tmp, result_tmp;
744
745 res = 0;
746 ZEND_HASH_FOREACH_STR_KEY(ht, key) {
747 ZVAL_STR(&key_tmp, key);
748 compare_function(&result_tmp, op1, &key_tmp);
749 if (Z_LVAL(result_tmp) == 0) {
750 res = 1;
751 break;
752 }
753 } ZEND_HASH_FOREACH_END();
754 }
755 ZVAL_BOOL(result, res);
756 return SUCCESS;
757 }
758
ct_eval_array_key_exists(zval * result,zval * op1,zval * op2)759 static inline int ct_eval_array_key_exists(zval *result, zval *op1, zval *op2) {
760 zval *value;
761
762 if (Z_TYPE_P(op2) != IS_ARRAY && !IS_PARTIAL_ARRAY(op2)) {
763 return FAILURE;
764 }
765 if (Z_TYPE_P(op1) != IS_STRING && Z_TYPE_P(op1) != IS_LONG && Z_TYPE_P(op1) != IS_NULL) {
766 return FAILURE;
767 }
768 if (fetch_array_elem(&value, op2, op1) == FAILURE) {
769 return FAILURE;
770 }
771 if (IS_PARTIAL_ARRAY(op2) && (!value || IS_BOT(value))) {
772 return FAILURE;
773 }
774
775 ZVAL_BOOL(result, value != NULL);
776 return SUCCESS;
777 }
778
779 /* The functions chosen here are simple to implement and either likely to affect a branch,
780 * or just happened to be commonly used with constant operands in WP (need to test other
781 * applications as well, of course). */
ct_eval_func_call(zval * result,zend_string * name,uint32_t num_args,zval ** args)782 static inline int ct_eval_func_call(
783 zval *result, zend_string *name, uint32_t num_args, zval **args) {
784 uint32_t i;
785 zend_execute_data *execute_data, *prev_execute_data;
786 zend_function *func;
787 int overflow;
788
789 if (num_args == 0) {
790 if (zend_string_equals_literal(name, "get_magic_quotes_gpc")
791 || zend_string_equals_literal(name, "get_magic_quotes_gpc_runtime")
792 || zend_string_equals_literal(name, "php_sapi_name")
793 || zend_string_equals_literal(name, "imagetypes")
794 || zend_string_equals_literal(name, "phpversion")) {
795 /* pass */
796 } else {
797 return FAILURE;
798 }
799 } else if (num_args == 1) {
800 if (zend_string_equals_literal(name, "chr")) {
801 zend_long c;
802 if (Z_TYPE_P(args[0]) != IS_LONG) {
803 return FAILURE;
804 }
805
806 c = Z_LVAL_P(args[0]) & 0xff;
807 ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
808 return SUCCESS;
809 } else if (zend_string_equals_literal(name, "count")) {
810 if (Z_TYPE_P(args[0]) != IS_ARRAY) {
811 return FAILURE;
812 }
813
814 ZVAL_LONG(result, zend_hash_num_elements(Z_ARRVAL_P(args[0])));
815 return SUCCESS;
816 } else if (zend_string_equals_literal(name, "ini_get")) {
817 zend_ini_entry *ini_entry;
818
819 if (Z_TYPE_P(args[0]) != IS_STRING) {
820 return FAILURE;
821 }
822
823 ini_entry = zend_hash_find_ptr(EG(ini_directives), Z_STR_P(args[0]));
824 if (!ini_entry) {
825 ZVAL_FALSE(result);
826 } else if (ini_entry->modifiable != ZEND_INI_SYSTEM) {
827 return FAILURE;
828 } else if (ini_entry->value) {
829 ZVAL_STR_COPY(result, ini_entry->value);
830 } else {
831 ZVAL_EMPTY_STRING(result);
832 }
833 return SUCCESS;
834 } else if (zend_string_equals_literal(name, "trim")
835 || zend_string_equals_literal(name, "rtrim")
836 || zend_string_equals_literal(name, "ltrim")
837 || zend_string_equals_literal(name, "str_split")
838 || zend_string_equals_literal(name, "preg_quote")
839 || zend_string_equals_literal(name, "base64_encode")
840 || zend_string_equals_literal(name, "base64_decode")
841 || zend_string_equals_literal(name, "urlencode")
842 || zend_string_equals_literal(name, "urldecode")
843 || zend_string_equals_literal(name, "rawurlencode")
844 || zend_string_equals_literal(name, "rawurldecode")
845 || zend_string_equals_literal(name, "php_uname")) {
846 if (Z_TYPE_P(args[0]) != IS_STRING) {
847 return FAILURE;
848 }
849 /* pass */
850 } else if (zend_string_equals_literal(name, "array_keys")
851 || zend_string_equals_literal(name, "array_values")) {
852 if (Z_TYPE_P(args[0]) != IS_ARRAY) {
853 return FAILURE;
854 }
855 /* pass */
856 } else if (zend_string_equals_literal(name, "array_flip")) {
857 zval *entry;
858
859 if (Z_TYPE_P(args[0]) != IS_ARRAY) {
860 return FAILURE;
861 }
862 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
863 if (Z_TYPE_P(entry) != IS_LONG && Z_TYPE_P(entry) != IS_STRING) {
864 return FAILURE;
865 }
866 } ZEND_HASH_FOREACH_END();
867 /* pass */
868 } else if (zend_string_equals_literal(name, "implode")) {
869 zval *entry;
870
871 if (Z_TYPE_P(args[0]) != IS_ARRAY) {
872 return FAILURE;
873 }
874
875 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
876 if (Z_TYPE_P(entry) > IS_STRING) {
877 return FAILURE;
878 }
879 } ZEND_HASH_FOREACH_END();
880 /* pass */
881 } else if (zend_string_equals_literal(name, "serialize")) {
882 /* pass */
883 } else {
884 return FAILURE;
885 }
886 } else if (num_args == 2) {
887 if (zend_string_equals_literal(name, "in_array")) {
888 if (Z_TYPE_P(args[1]) != IS_ARRAY) {
889 return FAILURE;
890 }
891 /* pass */
892 } else if (zend_string_equals_literal(name, "strpos")) {
893 if (Z_TYPE_P(args[0]) != IS_STRING
894 || Z_TYPE_P(args[1]) != IS_STRING
895 || !Z_STRLEN_P(args[1])
896 || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
897 return FAILURE;
898 }
899 /* pass */
900 } else if (zend_string_equals_literal(name, "str_split")) {
901 if (Z_TYPE_P(args[0]) != IS_STRING
902 || Z_TYPE_P(args[1]) != IS_LONG
903 || Z_LVAL_P(args[1]) <= 0) {
904 return FAILURE;
905 }
906 /* pass */
907 } else if (zend_string_equals_literal(name, "array_key_exists")) {
908 if (Z_TYPE_P(args[1]) != IS_ARRAY
909 || (Z_TYPE_P(args[0]) != IS_LONG
910 && Z_TYPE_P(args[0]) != IS_STRING
911 && Z_TYPE_P(args[0]) != IS_NULL)) {
912 return FAILURE;
913 }
914 /* pass */
915 } else if (zend_string_equals_literal(name, "trim")
916 || zend_string_equals_literal(name, "rtrim")
917 || zend_string_equals_literal(name, "ltrim")
918 || zend_string_equals_literal(name, "preg_quote")) {
919 if (Z_TYPE_P(args[0]) != IS_STRING
920 || Z_TYPE_P(args[1]) != IS_STRING) {
921 return FAILURE;
922 }
923 /* pass */
924 } else if (zend_string_equals_literal(name, "str_repeat")) {
925 if (Z_TYPE_P(args[0]) != IS_STRING
926 || Z_TYPE_P(args[1]) != IS_LONG
927 || zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) > 64 * 1024
928 || overflow) {
929 return FAILURE;
930 }
931 /* pass */
932 } else if (zend_string_equals_literal(name, "array_merge")
933 || zend_string_equals_literal(name, "array_replace")
934 || zend_string_equals_literal(name, "array_merge_recursive")
935 || zend_string_equals_literal(name, "array_replace_recursive")
936 || zend_string_equals_literal(name, "array_diff")
937 || zend_string_equals_literal(name, "array_diff_assoc")
938 || zend_string_equals_literal(name, "array_diff_key")) {
939 for (i = 0; i < num_args; i++) {
940 if (Z_TYPE_P(args[i]) != IS_ARRAY) {
941 return FAILURE;
942 }
943 }
944 /* pass */
945 } else if (zend_string_equals_literal(name, "implode")) {
946 zval *entry;
947
948 if ((Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_ARRAY)
949 && (Z_TYPE_P(args[0]) != IS_ARRAY || Z_TYPE_P(args[1]) != IS_STRING)) {
950 return FAILURE;
951 }
952
953 if (Z_TYPE_P(args[0]) == IS_ARRAY) {
954 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
955 if (Z_TYPE_P(entry) > IS_STRING) {
956 return FAILURE;
957 }
958 } ZEND_HASH_FOREACH_END();
959 } else {
960 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[1]), entry) {
961 if (Z_TYPE_P(entry) > IS_STRING) {
962 return FAILURE;
963 }
964 } ZEND_HASH_FOREACH_END();
965 }
966 /* pass */
967 } else if (zend_string_equals_literal(name, "version_compare")) {
968 if (Z_TYPE_P(args[0]) != IS_STRING
969 || Z_TYPE_P(args[1]) != IS_STRING) {
970 return FAILURE;
971 }
972 /* pass */
973 } else if (zend_string_equals_literal(name, "substr")) {
974 if (Z_TYPE_P(args[0]) != IS_STRING
975 || Z_TYPE_P(args[1]) != IS_LONG
976 || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
977 return FAILURE;
978 }
979 /* pass */
980 } else if (zend_string_equals_literal(name, "pow")) {
981 if ((Z_TYPE_P(args[0]) != IS_LONG && Z_TYPE_P(args[0]) != IS_DOUBLE)
982 || (Z_TYPE_P(args[1]) != IS_LONG && Z_TYPE_P(args[1]) != IS_DOUBLE)) {
983 return FAILURE;
984 }
985 /* pass */
986 } else {
987 return FAILURE;
988 }
989 } else if (num_args == 3) {
990 if (zend_string_equals_literal(name, "in_array")) {
991 if (Z_TYPE_P(args[1]) != IS_ARRAY
992 || (Z_TYPE_P(args[2]) != IS_FALSE
993 && Z_TYPE_P(args[2]) != IS_TRUE)) {
994 return FAILURE;
995 }
996 /* pass */
997 } else if (zend_string_equals_literal(name, "array_merge")
998 || zend_string_equals_literal(name, "array_replace")
999 || zend_string_equals_literal(name, "array_merge_recursive")
1000 || zend_string_equals_literal(name, "array_replace_recursive")
1001 || zend_string_equals_literal(name, "array_diff")
1002 || zend_string_equals_literal(name, "array_diff_assoc")
1003 || zend_string_equals_literal(name, "array_diff_key")) {
1004 for (i = 0; i < num_args; i++) {
1005 if (Z_TYPE_P(args[i]) != IS_ARRAY) {
1006 return FAILURE;
1007 }
1008 }
1009 /* pass */
1010 } else if (zend_string_equals_literal(name, "version_compare")) {
1011 if (Z_TYPE_P(args[0]) != IS_STRING
1012 || Z_TYPE_P(args[1]) != IS_STRING
1013 || Z_TYPE_P(args[2]) != IS_STRING) {
1014 return FAILURE;
1015 }
1016 /* pass */
1017 } else if (zend_string_equals_literal(name, "substr")) {
1018 if (Z_TYPE_P(args[0]) != IS_STRING
1019 || Z_TYPE_P(args[1]) != IS_LONG
1020 || Z_TYPE_P(args[2]) != IS_LONG
1021 || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
1022 return FAILURE;
1023 }
1024 /* pass */
1025 } else {
1026 return FAILURE;
1027 }
1028 } else {
1029 return FAILURE;
1030 }
1031
1032 func = zend_hash_find_ptr(CG(function_table), name);
1033 if (!func || func->type != ZEND_INTERNAL_FUNCTION
1034 || func->internal_function.handler == ZEND_FN(display_disabled_function)) {
1035 return FAILURE;
1036 }
1037
1038 execute_data = safe_emalloc(num_args, sizeof(zval), ZEND_CALL_FRAME_SLOT * sizeof(zval));
1039 memset(execute_data, 0, sizeof(zend_execute_data));
1040 prev_execute_data = EG(current_execute_data);
1041 EG(current_execute_data) = execute_data;
1042
1043 EX(func) = func;
1044 EX_NUM_ARGS() = num_args;
1045 for (i = 0; i < num_args; i++) {
1046 ZVAL_COPY(EX_VAR_NUM(i), args[i]);
1047 }
1048 func->internal_function.handler(execute_data, result);
1049 for (i = 0; i < num_args; i++) {
1050 zval_ptr_dtor_nogc(EX_VAR_NUM(i));
1051 }
1052 efree(execute_data);
1053 EG(current_execute_data) = prev_execute_data;
1054 return SUCCESS;
1055 }
1056
1057 #define SET_RESULT(op, zv) do { \
1058 if (ssa_op->op##_def >= 0) { \
1059 set_value(scdf, ctx, ssa_op->op##_def, zv); \
1060 } \
1061 } while (0)
1062 #define SET_RESULT_BOT(op) SET_RESULT(op, &ctx->bot)
1063 #define SET_RESULT_TOP(op) SET_RESULT(op, &ctx->top)
1064
1065 #define SKIP_IF_TOP(op) if (IS_TOP(op)) return;
1066
sccp_visit_instr(scdf_ctx * scdf,zend_op * opline,zend_ssa_op * ssa_op)1067 static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) {
1068 sccp_ctx *ctx = (sccp_ctx *) scdf;
1069 zval *op1, *op2, zv; /* zv is a temporary to hold result values */
1070
1071 op1 = get_op1_value(ctx, opline, ssa_op);
1072 op2 = get_op2_value(ctx, opline, ssa_op);
1073
1074 switch (opline->opcode) {
1075 case ZEND_ASSIGN:
1076 /* The value of op1 is irrelevant here, because we are overwriting it
1077 * -- unless it can be a reference, in which case we propagate a BOT. */
1078 if (IS_BOT(op1) && (ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF)) {
1079 SET_RESULT_BOT(op1);
1080 } else {
1081 SET_RESULT(op1, op2);
1082 }
1083
1084 SET_RESULT(result, op2);
1085 return;
1086 case ZEND_TYPE_CHECK:
1087 /* We may be able to evaluate TYPE_CHECK based on type inference info,
1088 * even if we don't know the precise value. */
1089 if (!value_known(op1)) {
1090 uint32_t type = ctx->scdf.ssa->var_info[ssa_op->op1_use].type;
1091 uint32_t expected_type_mask = opline->extended_value;
1092 if (!(type & expected_type_mask) && !(type & MAY_BE_UNDEF)) {
1093 ZVAL_FALSE(&zv);
1094 SET_RESULT(result, &zv);
1095 return;
1096 } else if (!(type & ((MAY_BE_ANY|MAY_BE_UNDEF) - expected_type_mask))
1097 && !(expected_type_mask & MAY_BE_RESOURCE)) {
1098 ZVAL_TRUE(&zv);
1099 SET_RESULT(result, &zv);
1100 return;
1101 }
1102 }
1103 break;
1104 case ZEND_ASSIGN_DIM:
1105 {
1106 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1107
1108 /* If $a in $a[$b]=$c is UNDEF, treat it like NULL. There is no warning. */
1109 if ((ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_ANY) == 0) {
1110 op1 = &EG(uninitialized_zval);
1111 }
1112
1113 if (IS_BOT(op1)) {
1114 SET_RESULT_BOT(result);
1115 SET_RESULT_BOT(op1);
1116 return;
1117 }
1118
1119 SKIP_IF_TOP(op1);
1120 SKIP_IF_TOP(data);
1121 if (op2) {
1122 SKIP_IF_TOP(op2);
1123 }
1124
1125 if (op2 && IS_BOT(op2)) {
1126 /* Update of unknown index */
1127 SET_RESULT_BOT(result);
1128 if (ssa_op->op1_def >= 0) {
1129 empty_partial_array(&zv);
1130 SET_RESULT(op1, &zv);
1131 zval_ptr_dtor_nogc(&zv);
1132 } else {
1133 SET_RESULT_BOT(op1);
1134 }
1135 return;
1136 }
1137
1138 if (IS_BOT(data)) {
1139
1140 SET_RESULT_BOT(result);
1141 if ((IS_PARTIAL_ARRAY(op1)
1142 || Z_TYPE_P(op1) == IS_NULL
1143 || Z_TYPE_P(op1) == IS_FALSE
1144 || Z_TYPE_P(op1) == IS_ARRAY)
1145 && ssa_op->op1_def >= 0) {
1146
1147 if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
1148 empty_partial_array(&zv);
1149 } else {
1150 dup_partial_array(&zv, op1);
1151 }
1152
1153 if (!op2) {
1154 /* We can't add NEXT element into partial array (skip it) */
1155 SET_RESULT(op1, &zv);
1156 } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
1157 SET_RESULT(op1, &zv);
1158 } else {
1159 SET_RESULT_BOT(op1);
1160 }
1161
1162 zval_ptr_dtor_nogc(&zv);
1163 } else {
1164 SET_RESULT_BOT(op1);
1165 }
1166
1167 } else {
1168
1169 if (IS_PARTIAL_ARRAY(op1)) {
1170 dup_partial_array(&zv, op1);
1171 } else {
1172 ZVAL_COPY(&zv, op1);
1173 }
1174
1175 if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
1176 /* We can't add NEXT element into partial array (skip it) */
1177 SET_RESULT(result, data);
1178 SET_RESULT(op1, &zv);
1179 } else if (ct_eval_assign_dim(&zv, data, op2) == SUCCESS) {
1180 /* Mark array containing partial array as partial */
1181 if (IS_PARTIAL_ARRAY(data)) {
1182 MAKE_PARTIAL_ARRAY(&zv);
1183 }
1184 SET_RESULT(result, data);
1185 SET_RESULT(op1, &zv);
1186 } else {
1187 SET_RESULT_BOT(result);
1188 SET_RESULT_BOT(op1);
1189 }
1190
1191 zval_ptr_dtor_nogc(&zv);
1192 }
1193 return;
1194 }
1195
1196 case ZEND_ASSIGN_OBJ:
1197 if (ssa_op->op1_def >= 0
1198 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1199 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1200 zend_ssa_var_info *var_info = &ctx->scdf.ssa->var_info[ssa_op->op1_use];
1201
1202 /* Don't try to propagate assignments to (potentially) typed properties. We would
1203 * need to deal with errors and type conversions first. */
1204 if (!var_info->ce || (var_info->ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
1205 SET_RESULT_BOT(result);
1206 SET_RESULT_BOT(op1);
1207 return;
1208 }
1209
1210 /* If $a in $a->foo=$c is UNDEF, treat it like NULL. There is no warning. */
1211 if ((var_info->type & MAY_BE_ANY) == 0) {
1212 op1 = &EG(uninitialized_zval);
1213 }
1214
1215 if (IS_BOT(op1)) {
1216 SET_RESULT_BOT(result);
1217 SET_RESULT_BOT(op1);
1218 return;
1219 }
1220
1221 SKIP_IF_TOP(op1);
1222 SKIP_IF_TOP(data);
1223 SKIP_IF_TOP(op2);
1224
1225 if (IS_BOT(op2)) {
1226 /* Update of unknown property */
1227 SET_RESULT_BOT(result);
1228 empty_partial_object(&zv);
1229 SET_RESULT(op1, &zv);
1230 zval_ptr_dtor_nogc(&zv);
1231 return;
1232 }
1233
1234 if (IS_BOT(data)) {
1235 SET_RESULT_BOT(result);
1236 if (IS_PARTIAL_OBJECT(op1)
1237 || Z_TYPE_P(op1) == IS_NULL
1238 || Z_TYPE_P(op1) == IS_FALSE) {
1239
1240 if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
1241 empty_partial_object(&zv);
1242 } else {
1243 dup_partial_object(&zv, op1);
1244 }
1245
1246 if (ct_eval_del_obj_prop(&zv, op2) == SUCCESS) {
1247 SET_RESULT(op1, &zv);
1248 } else {
1249 SET_RESULT_BOT(op1);
1250 }
1251 zval_ptr_dtor_nogc(&zv);
1252 } else {
1253 SET_RESULT_BOT(op1);
1254 }
1255
1256 } else {
1257
1258 if (IS_PARTIAL_OBJECT(op1)) {
1259 dup_partial_object(&zv, op1);
1260 } else {
1261 ZVAL_COPY(&zv, op1);
1262 }
1263
1264 if (ct_eval_assign_obj(&zv, data, op2) == SUCCESS) {
1265 SET_RESULT(result, data);
1266 SET_RESULT(op1, &zv);
1267 } else {
1268 SET_RESULT_BOT(result);
1269 SET_RESULT_BOT(op1);
1270 }
1271
1272 zval_ptr_dtor_nogc(&zv);
1273 }
1274 } else {
1275 SET_RESULT_BOT(result);
1276 SET_RESULT_BOT(op1);
1277 }
1278 return;
1279
1280 case ZEND_SEND_VAL:
1281 case ZEND_SEND_VAR:
1282 {
1283 /* If the value of a SEND for an ICALL changes, we need to reconsider the
1284 * ICALL result value. Otherwise we can ignore the opcode. */
1285 zend_call_info *call;
1286 if (!ctx->call_map) {
1287 return;
1288 }
1289
1290 call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1291 if (IS_TOP(op1) || !call || !call->caller_call_opline
1292 || call->caller_call_opline->opcode != ZEND_DO_ICALL) {
1293 return;
1294 }
1295
1296 opline = call->caller_call_opline;
1297 ssa_op = &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes];
1298 break;
1299 }
1300 case ZEND_INIT_ARRAY:
1301 case ZEND_ADD_ARRAY_ELEMENT:
1302 {
1303 zval *result = NULL;
1304
1305 if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
1306 result = &ctx->values[ssa_op->result_use];
1307 if (IS_BOT(result)) {
1308 SET_RESULT_BOT(result);
1309 SET_RESULT_BOT(op1);
1310 return;
1311 }
1312 SKIP_IF_TOP(result);
1313 }
1314
1315 if (op1) {
1316 SKIP_IF_TOP(op1);
1317 }
1318
1319 if (op2) {
1320 SKIP_IF_TOP(op2);
1321 }
1322
1323 /* We want to avoid keeping around intermediate arrays for each SSA variable in the
1324 * ADD_ARRAY_ELEMENT chain. We do this by only keeping the array on the last opcode
1325 * and use a NULL value everywhere else. */
1326 if (result && Z_TYPE_P(result) == IS_NULL) {
1327 SET_RESULT_BOT(result);
1328 return;
1329 }
1330
1331 if (op2 && IS_BOT(op2)) {
1332 /* Update of unknown index */
1333 SET_RESULT_BOT(op1);
1334 if (ssa_op->result_def >= 0) {
1335 empty_partial_array(&zv);
1336 SET_RESULT(result, &zv);
1337 zval_ptr_dtor_nogc(&zv);
1338 } else {
1339 SET_RESULT_BOT(result);
1340 }
1341 return;
1342 }
1343
1344 if ((op1 && IS_BOT(op1))
1345 || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
1346
1347 SET_RESULT_BOT(op1);
1348 if (ssa_op->result_def >= 0) {
1349 if (!result) {
1350 empty_partial_array(&zv);
1351 } else {
1352 MAKE_PARTIAL_ARRAY(result);
1353 ZVAL_COPY_VALUE(&zv, result);
1354 ZVAL_NULL(result);
1355 }
1356 if (!op2) {
1357 /* We can't add NEXT element into partial array (skip it) */
1358 SET_RESULT(result, &zv);
1359 } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
1360 SET_RESULT(result, &zv);
1361 } else {
1362 SET_RESULT_BOT(result);
1363 }
1364 zval_ptr_dtor_nogc(&zv);
1365 } else {
1366 /* If any operand is BOT, mark the result as BOT right away.
1367 * Exceptions to this rule are handled above. */
1368 SET_RESULT_BOT(result);
1369 }
1370
1371 } else {
1372 if (result) {
1373 ZVAL_COPY_VALUE(&zv, result);
1374 ZVAL_NULL(result);
1375 } else {
1376 array_init(&zv);
1377 }
1378
1379 if (op1) {
1380 if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
1381 /* We can't add NEXT element into partial array (skip it) */
1382 SET_RESULT(result, &zv);
1383 } else if (ct_eval_add_array_elem(&zv, op1, op2) == SUCCESS) {
1384 if (IS_PARTIAL_ARRAY(op1)) {
1385 MAKE_PARTIAL_ARRAY(&zv);
1386 }
1387 SET_RESULT(result, &zv);
1388 } else {
1389 SET_RESULT_BOT(result);
1390 }
1391 } else {
1392 SET_RESULT(result, &zv);
1393 }
1394
1395 zval_ptr_dtor_nogc(&zv);
1396 }
1397 return;
1398 }
1399 case ZEND_ADD_ARRAY_UNPACK: {
1400 zval *result = &ctx->values[ssa_op->result_use];
1401 if (IS_BOT(result) || IS_BOT(op1)) {
1402 SET_RESULT_BOT(result);
1403 return;
1404 }
1405 SKIP_IF_TOP(result);
1406 SKIP_IF_TOP(op1);
1407
1408 /* See comment for ADD_ARRAY_ELEMENT. */
1409 if (Z_TYPE_P(result) == IS_NULL) {
1410 SET_RESULT_BOT(result);
1411 return;
1412 }
1413 ZVAL_COPY_VALUE(&zv, result);
1414 ZVAL_NULL(result);
1415
1416 if (ct_eval_add_array_unpack(&zv, op1) == SUCCESS) {
1417 SET_RESULT(result, &zv);
1418 } else {
1419 SET_RESULT_BOT(result);
1420 }
1421 zval_ptr_dtor_nogc(&zv);
1422 return;
1423 }
1424 case ZEND_NEW:
1425 if (ssa_op->result_def >= 0
1426 && ctx->scdf.ssa->vars[ssa_op->result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1427 empty_partial_object(&zv);
1428 SET_RESULT(result, &zv);
1429 zval_ptr_dtor_nogc(&zv);
1430 } else {
1431 SET_RESULT_BOT(result);
1432 }
1433 return;
1434 case ZEND_ASSIGN_STATIC_PROP_REF:
1435 case ZEND_ASSIGN_OBJ_REF:
1436 /* Handled here because we also need to BOT the OP_DATA operand, while the generic
1437 * code below will not do so. */
1438 SET_RESULT_BOT(result);
1439 SET_RESULT_BOT(op1);
1440 SET_RESULT_BOT(op2);
1441 opline++;
1442 ssa_op++;
1443 SET_RESULT_BOT(op1);
1444 break;
1445 }
1446
1447 if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) {
1448 /* If any operand is BOT, mark the result as BOT right away.
1449 * Exceptions to this rule are handled above. */
1450 SET_RESULT_BOT(result);
1451 SET_RESULT_BOT(op1);
1452 SET_RESULT_BOT(op2);
1453 return;
1454 }
1455
1456 switch (opline->opcode) {
1457 case ZEND_ADD:
1458 case ZEND_SUB:
1459 case ZEND_MUL:
1460 case ZEND_DIV:
1461 case ZEND_MOD:
1462 case ZEND_POW:
1463 case ZEND_SL:
1464 case ZEND_SR:
1465 case ZEND_CONCAT:
1466 case ZEND_FAST_CONCAT:
1467 case ZEND_IS_EQUAL:
1468 case ZEND_IS_NOT_EQUAL:
1469 case ZEND_IS_SMALLER:
1470 case ZEND_IS_SMALLER_OR_EQUAL:
1471 case ZEND_IS_IDENTICAL:
1472 case ZEND_IS_NOT_IDENTICAL:
1473 case ZEND_BW_OR:
1474 case ZEND_BW_AND:
1475 case ZEND_BW_XOR:
1476 case ZEND_BOOL_XOR:
1477 case ZEND_CASE:
1478 SKIP_IF_TOP(op1);
1479 SKIP_IF_TOP(op2);
1480
1481 if (ct_eval_binary_op(&zv, opline->opcode, op1, op2) == SUCCESS) {
1482 SET_RESULT(result, &zv);
1483 zval_ptr_dtor_nogc(&zv);
1484 break;
1485 }
1486 SET_RESULT_BOT(result);
1487 break;
1488 case ZEND_ASSIGN_OP:
1489 case ZEND_ASSIGN_DIM_OP:
1490 case ZEND_ASSIGN_OBJ_OP:
1491 case ZEND_ASSIGN_STATIC_PROP_OP:
1492 if (op1) {
1493 SKIP_IF_TOP(op1);
1494 }
1495 if (op2) {
1496 SKIP_IF_TOP(op2);
1497 }
1498 if (opline->opcode == ZEND_ASSIGN_OP) {
1499 if (ct_eval_binary_op(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1500 SET_RESULT(op1, &zv);
1501 SET_RESULT(result, &zv);
1502 zval_ptr_dtor_nogc(&zv);
1503 break;
1504 }
1505 } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
1506 if ((IS_PARTIAL_ARRAY(op1) || Z_TYPE_P(op1) == IS_ARRAY)
1507 && ssa_op->op1_def >= 0 && op2) {
1508 zval tmp;
1509 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1510
1511 SKIP_IF_TOP(data);
1512
1513 if (ct_eval_fetch_dim(&tmp, op1, op2, 0) == SUCCESS) {
1514 if (IS_BOT(data)) {
1515 dup_partial_array(&zv, op1);
1516 ct_eval_del_array_elem(&zv, op2);
1517 SET_RESULT_BOT(result);
1518 SET_RESULT(op1, &zv);
1519 zval_ptr_dtor_nogc(&tmp);
1520 zval_ptr_dtor_nogc(&zv);
1521 break;
1522 }
1523
1524 if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) != SUCCESS) {
1525 SET_RESULT_BOT(result);
1526 SET_RESULT_BOT(op1);
1527 zval_ptr_dtor_nogc(&tmp);
1528 break;
1529 }
1530
1531 if (IS_PARTIAL_ARRAY(op1)) {
1532 dup_partial_array(&zv, op1);
1533 } else {
1534 ZVAL_COPY(&zv, op1);
1535 }
1536
1537 if (ct_eval_assign_dim(&zv, &tmp, op2) == SUCCESS) {
1538 SET_RESULT(result, &tmp);
1539 SET_RESULT(op1, &zv);
1540 zval_ptr_dtor_nogc(&tmp);
1541 zval_ptr_dtor_nogc(&zv);
1542 break;
1543 }
1544
1545 zval_ptr_dtor_nogc(&tmp);
1546 zval_ptr_dtor_nogc(&zv);
1547 }
1548 }
1549 } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
1550 if (op1 && IS_PARTIAL_OBJECT(op1)
1551 && ssa_op->op1_def >= 0
1552 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1553 zval tmp;
1554 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1555
1556 SKIP_IF_TOP(data);
1557
1558 if (ct_eval_fetch_obj(&tmp, op1, op2) == SUCCESS) {
1559 if (IS_BOT(data)) {
1560 dup_partial_object(&zv, op1);
1561 ct_eval_del_obj_prop(&zv, op2);
1562 SET_RESULT_BOT(result);
1563 SET_RESULT(op1, &zv);
1564 zval_ptr_dtor_nogc(&tmp);
1565 zval_ptr_dtor_nogc(&zv);
1566 break;
1567 }
1568
1569 if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) != SUCCESS) {
1570 SET_RESULT_BOT(result);
1571 SET_RESULT_BOT(op1);
1572 zval_ptr_dtor_nogc(&tmp);
1573 break;
1574 }
1575
1576 dup_partial_object(&zv, op1);
1577
1578 if (ct_eval_assign_obj(&zv, &tmp, op2) == SUCCESS) {
1579 SET_RESULT(result, &tmp);
1580 SET_RESULT(op1, &zv);
1581 zval_ptr_dtor_nogc(&tmp);
1582 zval_ptr_dtor_nogc(&zv);
1583 break;
1584 }
1585
1586 zval_ptr_dtor_nogc(&tmp);
1587 zval_ptr_dtor_nogc(&zv);
1588 }
1589 }
1590 }
1591 SET_RESULT_BOT(result);
1592 SET_RESULT_BOT(op1);
1593 break;
1594 case ZEND_PRE_INC_OBJ:
1595 case ZEND_PRE_DEC_OBJ:
1596 case ZEND_POST_INC_OBJ:
1597 case ZEND_POST_DEC_OBJ:
1598 if (op1) {
1599 SKIP_IF_TOP(op1);
1600 SKIP_IF_TOP(op2);
1601 if (IS_PARTIAL_OBJECT(op1)
1602 && ssa_op->op1_def >= 0
1603 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1604 zval tmp1, tmp2;
1605
1606 if (ct_eval_fetch_obj(&tmp1, op1, op2) == SUCCESS
1607 && ct_eval_incdec(&tmp2, opline->opcode, &tmp1) == SUCCESS) {
1608
1609 dup_partial_object(&zv, op1);
1610 ct_eval_assign_obj(&zv, &tmp2, op2);
1611 if (opline->opcode == ZEND_PRE_INC_OBJ
1612 || opline->opcode == ZEND_PRE_DEC_OBJ) {
1613 SET_RESULT(result, &tmp2);
1614 } else {
1615 SET_RESULT(result, &tmp1);
1616 }
1617 SET_RESULT(op1, &zv);
1618 zval_ptr_dtor_nogc(&zv);
1619 break;
1620 }
1621 }
1622 }
1623 SET_RESULT_BOT(op1);
1624 SET_RESULT_BOT(result);
1625 break;
1626 case ZEND_PRE_INC:
1627 case ZEND_PRE_DEC:
1628 SKIP_IF_TOP(op1);
1629 if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1630 SET_RESULT(op1, &zv);
1631 SET_RESULT(result, &zv);
1632 zval_ptr_dtor_nogc(&zv);
1633 break;
1634 }
1635 SET_RESULT_BOT(op1);
1636 SET_RESULT_BOT(result);
1637 break;
1638 case ZEND_POST_INC:
1639 case ZEND_POST_DEC:
1640 SKIP_IF_TOP(op1);
1641 SET_RESULT(result, op1);
1642 if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1643 SET_RESULT(op1, &zv);
1644 zval_ptr_dtor_nogc(&zv);
1645 break;
1646 }
1647 SET_RESULT_BOT(op1);
1648 break;
1649 case ZEND_BW_NOT:
1650 case ZEND_BOOL_NOT:
1651 SKIP_IF_TOP(op1);
1652 if (IS_PARTIAL_ARRAY(op1)) {
1653 SET_RESULT_BOT(result);
1654 break;
1655 }
1656 if (zend_optimizer_eval_unary_op(&zv, opline->opcode, op1) == SUCCESS) {
1657 SET_RESULT(result, &zv);
1658 zval_ptr_dtor_nogc(&zv);
1659 break;
1660 }
1661 SET_RESULT_BOT(result);
1662 break;
1663 case ZEND_CAST:
1664 SKIP_IF_TOP(op1);
1665 if (IS_PARTIAL_ARRAY(op1)) {
1666 SET_RESULT_BOT(result);
1667 break;
1668 }
1669 if (zend_optimizer_eval_cast(&zv, opline->extended_value, op1) == SUCCESS) {
1670 SET_RESULT(result, &zv);
1671 zval_ptr_dtor_nogc(&zv);
1672 break;
1673 }
1674 SET_RESULT_BOT(result);
1675 break;
1676 case ZEND_BOOL:
1677 case ZEND_JMPZ_EX:
1678 case ZEND_JMPNZ_EX:
1679 SKIP_IF_TOP(op1);
1680 if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
1681 SET_RESULT(result, &zv);
1682 zval_ptr_dtor_nogc(&zv);
1683 break;
1684 }
1685 SET_RESULT_BOT(result);
1686 break;
1687 case ZEND_STRLEN:
1688 SKIP_IF_TOP(op1);
1689 if (zend_optimizer_eval_strlen(&zv, op1) == SUCCESS) {
1690 SET_RESULT(result, &zv);
1691 zval_ptr_dtor_nogc(&zv);
1692 break;
1693 }
1694 SET_RESULT_BOT(result);
1695 break;
1696 case ZEND_COUNT:
1697 SKIP_IF_TOP(op1);
1698 if (Z_TYPE_P(op1) == IS_ARRAY) {
1699 ZVAL_LONG(&zv, zend_hash_num_elements(Z_ARRVAL_P(op1)));
1700 SET_RESULT(result, &zv);
1701 zval_ptr_dtor_nogc(&zv);
1702 break;
1703 }
1704 SET_RESULT_BOT(result);
1705 break;
1706 case ZEND_IN_ARRAY:
1707 SKIP_IF_TOP(op1);
1708 SKIP_IF_TOP(op2);
1709 if (ct_eval_in_array(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1710 SET_RESULT(result, &zv);
1711 zval_ptr_dtor_nogc(&zv);
1712 break;
1713 }
1714 SET_RESULT_BOT(result);
1715 break;
1716 case ZEND_ARRAY_KEY_EXISTS:
1717 SKIP_IF_TOP(op1);
1718 SKIP_IF_TOP(op2);
1719 if (ct_eval_array_key_exists(&zv, op1, op2) == SUCCESS) {
1720 SET_RESULT(result, &zv);
1721 zval_ptr_dtor_nogc(&zv);
1722 break;
1723 }
1724 SET_RESULT_BOT(result);
1725 break;
1726 case ZEND_FETCH_DIM_R:
1727 case ZEND_FETCH_DIM_IS:
1728 case ZEND_FETCH_LIST_R:
1729 SKIP_IF_TOP(op1);
1730 SKIP_IF_TOP(op2);
1731
1732 if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST_R)) == SUCCESS) {
1733 SET_RESULT(result, &zv);
1734 zval_ptr_dtor_nogc(&zv);
1735 break;
1736 }
1737 SET_RESULT_BOT(result);
1738 break;
1739 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
1740 SKIP_IF_TOP(op1);
1741 SKIP_IF_TOP(op2);
1742
1743 if (ct_eval_isset_dim(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1744 SET_RESULT(result, &zv);
1745 zval_ptr_dtor_nogc(&zv);
1746 break;
1747 }
1748 SET_RESULT_BOT(result);
1749 break;
1750 case ZEND_FETCH_OBJ_R:
1751 case ZEND_FETCH_OBJ_IS:
1752 if (op1) {
1753 SKIP_IF_TOP(op1);
1754 SKIP_IF_TOP(op2);
1755
1756 if (ct_eval_fetch_obj(&zv, op1, op2) == SUCCESS) {
1757 SET_RESULT(result, &zv);
1758 zval_ptr_dtor_nogc(&zv);
1759 break;
1760 }
1761 }
1762 SET_RESULT_BOT(result);
1763 break;
1764 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
1765 if (op1) {
1766 SKIP_IF_TOP(op1);
1767 SKIP_IF_TOP(op2);
1768
1769 if (ct_eval_isset_obj(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1770 SET_RESULT(result, &zv);
1771 zval_ptr_dtor_nogc(&zv);
1772 break;
1773 }
1774 }
1775 SET_RESULT_BOT(result);
1776 break;
1777 case ZEND_QM_ASSIGN:
1778 case ZEND_JMP_SET:
1779 case ZEND_COALESCE:
1780 case ZEND_COPY_TMP:
1781 SET_RESULT(result, op1);
1782 break;
1783 #if 0
1784 case ZEND_FETCH_CLASS:
1785 if (!op1) {
1786 SET_RESULT_BOT(result);
1787 break;
1788 }
1789 SET_RESULT(result, op1);
1790 break;
1791 #endif
1792 case ZEND_ISSET_ISEMPTY_CV:
1793 SKIP_IF_TOP(op1);
1794 if (ct_eval_isset_isempty(&zv, opline->extended_value, op1) == SUCCESS) {
1795 SET_RESULT(result, &zv);
1796 zval_ptr_dtor_nogc(&zv);
1797 break;
1798 }
1799 SET_RESULT_BOT(result);
1800 break;
1801 case ZEND_TYPE_CHECK:
1802 SKIP_IF_TOP(op1);
1803 ct_eval_type_check(&zv, opline->extended_value, op1);
1804 SET_RESULT(result, &zv);
1805 zval_ptr_dtor_nogc(&zv);
1806 break;
1807 case ZEND_INSTANCEOF:
1808 SKIP_IF_TOP(op1);
1809 ZVAL_FALSE(&zv);
1810 SET_RESULT(result, &zv);
1811 break;
1812 case ZEND_ROPE_INIT:
1813 SKIP_IF_TOP(op2);
1814 if (IS_PARTIAL_ARRAY(op2)) {
1815 SET_RESULT_BOT(result);
1816 break;
1817 }
1818 if (zend_optimizer_eval_cast(&zv, IS_STRING, op2) == SUCCESS) {
1819 SET_RESULT(result, &zv);
1820 zval_ptr_dtor_nogc(&zv);
1821 break;
1822 }
1823 SET_RESULT_BOT(result);
1824 break;
1825 case ZEND_ROPE_ADD:
1826 case ZEND_ROPE_END:
1827 // TODO The way this is currently implemented will result in quadratic runtime
1828 // This is not necessary, the way the algorithm works it's okay to reuse the same
1829 // string for all SSA vars with some extra checks
1830 SKIP_IF_TOP(op1);
1831 SKIP_IF_TOP(op2);
1832 if (ct_eval_binary_op(&zv, ZEND_CONCAT, op1, op2) == SUCCESS) {
1833 SET_RESULT(result, &zv);
1834 zval_ptr_dtor_nogc(&zv);
1835 break;
1836 }
1837 SET_RESULT_BOT(result);
1838 break;
1839 case ZEND_DO_ICALL:
1840 {
1841 zend_call_info *call;
1842 zval *name, *args[3] = {NULL};
1843 int i;
1844
1845 if (!ctx->call_map) {
1846 SET_RESULT_BOT(result);
1847 break;
1848 }
1849
1850 call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1851 name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
1852
1853 /* We already know it can't be evaluated, don't bother checking again */
1854 if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
1855 break;
1856 }
1857
1858 /* We're only interested in functions with up to three arguments right now */
1859 if (call->num_args > 3) {
1860 SET_RESULT_BOT(result);
1861 break;
1862 }
1863
1864 for (i = 0; i < call->num_args; i++) {
1865 zend_op *opline = call->arg_info[i].opline;
1866 if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) {
1867 SET_RESULT_BOT(result);
1868 return;
1869 }
1870
1871 args[i] = get_op1_value(ctx, opline,
1872 &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
1873 if (args[i]) {
1874 if (IS_BOT(args[i]) || IS_PARTIAL_ARRAY(args[i])) {
1875 SET_RESULT_BOT(result);
1876 return;
1877 } else if (IS_TOP(args[i])) {
1878 return;
1879 }
1880 }
1881 }
1882
1883 /* We didn't get a BOT argument, so value stays the same */
1884 if (!IS_TOP(&ctx->values[ssa_op->result_def])) {
1885 break;
1886 }
1887
1888 if (ct_eval_func_call(&zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
1889 SET_RESULT(result, &zv);
1890 zval_ptr_dtor_nogc(&zv);
1891 break;
1892 }
1893
1894 #if 0
1895 /* sort out | uniq -c | sort -n */
1896 fprintf(stderr, "%s\n", Z_STRVAL_P(name));
1897 /*if (args[1]) {
1898 php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]);
1899 } else {
1900 php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]);
1901 }*/
1902 #endif
1903
1904 SET_RESULT_BOT(result);
1905 break;
1906 }
1907 default:
1908 {
1909 /* If we have no explicit implementation return BOT */
1910 SET_RESULT_BOT(result);
1911 SET_RESULT_BOT(op1);
1912 SET_RESULT_BOT(op2);
1913 break;
1914 }
1915 }
1916 }
1917
1918 /* Returns whether there is a successor */
sccp_mark_feasible_successors(scdf_ctx * scdf,int block_num,zend_basic_block * block,zend_op * opline,zend_ssa_op * ssa_op)1919 static void sccp_mark_feasible_successors(
1920 scdf_ctx *scdf,
1921 int block_num, zend_basic_block *block,
1922 zend_op *opline, zend_ssa_op *ssa_op) {
1923 sccp_ctx *ctx = (sccp_ctx *) scdf;
1924 zval *op1, zv;
1925 int s;
1926
1927 /* We can't determine the branch target at compile-time for these */
1928 switch (opline->opcode) {
1929 case ZEND_ASSERT_CHECK:
1930 case ZEND_CATCH:
1931 case ZEND_FE_FETCH_R:
1932 case ZEND_FE_FETCH_RW:
1933 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1934 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1935 return;
1936 }
1937
1938 op1 = get_op1_value(ctx, opline, ssa_op);
1939
1940 /* Branch target can be either one */
1941 if (!op1 || IS_BOT(op1)) {
1942 for (s = 0; s < block->successors_count; s++) {
1943 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1944 }
1945 return;
1946 }
1947
1948 /* Branch target not yet known */
1949 if (IS_TOP(op1)) {
1950 return;
1951 }
1952
1953 switch (opline->opcode) {
1954 case ZEND_JMPZ:
1955 case ZEND_JMPZNZ:
1956 case ZEND_JMPZ_EX:
1957 {
1958 if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
1959 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1960 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1961 return;
1962 }
1963 s = Z_TYPE(zv) == IS_TRUE;
1964 break;
1965 }
1966 case ZEND_JMPNZ:
1967 case ZEND_JMPNZ_EX:
1968 case ZEND_JMP_SET:
1969 {
1970 if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
1971 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1972 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1973 return;
1974 }
1975 s = Z_TYPE(zv) == IS_FALSE;
1976 break;
1977 }
1978 case ZEND_COALESCE:
1979 s = (Z_TYPE_P(op1) == IS_NULL);
1980 break;
1981 case ZEND_FE_RESET_R:
1982 case ZEND_FE_RESET_RW:
1983 /* A non-empty partial array is definitely non-empty, but an
1984 * empty partial array may be non-empty at runtime. */
1985 if (Z_TYPE_P(op1) != IS_ARRAY ||
1986 (IS_PARTIAL_ARRAY(op1) && zend_hash_num_elements(Z_ARR_P(op1)) == 0)) {
1987 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1988 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1989 return;
1990 }
1991 s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
1992 break;
1993 case ZEND_SWITCH_LONG:
1994 if (Z_TYPE_P(op1) == IS_LONG) {
1995 zend_op_array *op_array = scdf->op_array;
1996 zend_ssa *ssa = scdf->ssa;
1997 HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
1998 zval *jmp_zv = zend_hash_index_find(jmptable, Z_LVAL_P(op1));
1999 int target;
2000
2001 if (jmp_zv) {
2002 target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))];
2003 } else {
2004 target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
2005 }
2006 scdf_mark_edge_feasible(scdf, block_num, target);
2007 return;
2008 }
2009 s = block->successors_count - 1;
2010 break;
2011 case ZEND_SWITCH_STRING:
2012 if (Z_TYPE_P(op1) == IS_STRING) {
2013 zend_op_array *op_array = scdf->op_array;
2014 zend_ssa *ssa = scdf->ssa;
2015 HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
2016 zval *jmp_zv = zend_hash_find(jmptable, Z_STR_P(op1));
2017 int target;
2018
2019 if (jmp_zv) {
2020 target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))];
2021 } else {
2022 target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
2023 }
2024 scdf_mark_edge_feasible(scdf, block_num, target);
2025 return;
2026 }
2027 s = block->successors_count - 1;
2028 break;
2029 default:
2030 for (s = 0; s < block->successors_count; s++) {
2031 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
2032 }
2033 return;
2034 }
2035 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
2036 }
2037
join_hash_tables(HashTable * ret,HashTable * ht1,HashTable * ht2)2038 static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2)
2039 {
2040 zend_ulong index;
2041 zend_string *key;
2042 zval *val1, *val2;
2043
2044 ZEND_HASH_FOREACH_KEY_VAL(ht1, index, key, val1) {
2045 if (key) {
2046 val2 = zend_hash_find(ht2, key);
2047 } else {
2048 val2 = zend_hash_index_find(ht2, index);
2049 }
2050 if (val2 && zend_is_identical(val1, val2)) {
2051 if (key) {
2052 val1 = zend_hash_add_new(ret, key, val1);
2053 } else {
2054 val1 = zend_hash_index_add_new(ret, index, val1);
2055 }
2056 Z_TRY_ADDREF_P(val1);
2057 }
2058 } ZEND_HASH_FOREACH_END();
2059 }
2060
join_partial_arrays(zval * a,zval * b)2061 static int join_partial_arrays(zval *a, zval *b)
2062 {
2063 zval ret;
2064
2065 if ((Z_TYPE_P(a) != IS_ARRAY && !IS_PARTIAL_ARRAY(a))
2066 || (Z_TYPE_P(b) != IS_ARRAY && !IS_PARTIAL_ARRAY(b))) {
2067 return FAILURE;
2068 }
2069
2070 empty_partial_array(&ret);
2071 join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
2072 zval_ptr_dtor_nogc(a);
2073 ZVAL_COPY_VALUE(a, &ret);
2074
2075 return SUCCESS;
2076 }
2077
join_partial_objects(zval * a,zval * b)2078 static int join_partial_objects(zval *a, zval *b)
2079 {
2080 zval ret;
2081
2082 if (!IS_PARTIAL_OBJECT(a) || !IS_PARTIAL_OBJECT(b)) {
2083 return FAILURE;
2084 }
2085
2086 empty_partial_object(&ret);
2087 join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
2088 zval_ptr_dtor_nogc(a);
2089 ZVAL_COPY_VALUE(a, &ret);
2090
2091 return SUCCESS;
2092 }
2093
join_phi_values(zval * a,zval * b,zend_bool escape)2094 static void join_phi_values(zval *a, zval *b, zend_bool escape) {
2095 if (IS_BOT(a) || IS_TOP(b)) {
2096 return;
2097 }
2098 if (IS_TOP(a)) {
2099 zval_ptr_dtor_nogc(a);
2100 ZVAL_COPY(a, b);
2101 return;
2102 }
2103 if (IS_BOT(b)) {
2104 zval_ptr_dtor_nogc(a);
2105 MAKE_BOT(a);
2106 return;
2107 }
2108 if (IS_PARTIAL_ARRAY(a) || IS_PARTIAL_ARRAY(b)) {
2109 if (join_partial_arrays(a, b) != SUCCESS) {
2110 zval_ptr_dtor_nogc(a);
2111 MAKE_BOT(a);
2112 }
2113 } else if (IS_PARTIAL_OBJECT(a) || IS_PARTIAL_OBJECT(b)) {
2114 if (escape || join_partial_objects(a, b) != SUCCESS) {
2115 zval_ptr_dtor_nogc(a);
2116 MAKE_BOT(a);
2117 }
2118 } else if (!zend_is_identical(a, b)) {
2119 if (join_partial_arrays(a, b) != SUCCESS) {
2120 zval_ptr_dtor_nogc(a);
2121 MAKE_BOT(a);
2122 }
2123 }
2124 }
2125
sccp_visit_phi(scdf_ctx * scdf,zend_ssa_phi * phi)2126 static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
2127 sccp_ctx *ctx = (sccp_ctx *) scdf;
2128 zend_ssa *ssa = scdf->ssa;
2129 ZEND_ASSERT(phi->ssa_var >= 0);
2130 if (!IS_BOT(&ctx->values[phi->ssa_var])) {
2131 zend_basic_block *block = &ssa->cfg.blocks[phi->block];
2132 int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
2133
2134 int i;
2135 zval result;
2136 MAKE_TOP(&result);
2137 #if SCP_DEBUG
2138 fprintf(stderr, "Handling phi(");
2139 #endif
2140 if (phi->pi >= 0) {
2141 ZEND_ASSERT(phi->sources[0] >= 0);
2142 if (scdf_is_edge_feasible(scdf, phi->pi, phi->block)) {
2143 join_phi_values(&result, &ctx->values[phi->sources[0]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
2144 }
2145 } else {
2146 for (i = 0; i < block->predecessors_count; i++) {
2147 ZEND_ASSERT(phi->sources[i] >= 0);
2148 if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) {
2149 #if SCP_DEBUG
2150 scp_dump_value(&ctx->values[phi->sources[i]]);
2151 fprintf(stderr, ",");
2152 #endif
2153 join_phi_values(&result, &ctx->values[phi->sources[i]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
2154 } else {
2155 #if SCP_DEBUG
2156 fprintf(stderr, " --,");
2157 #endif
2158 }
2159 }
2160 }
2161 #if SCP_DEBUG
2162 fprintf(stderr, ")\n");
2163 #endif
2164
2165 set_value(scdf, ctx, phi->ssa_var, &result);
2166 zval_ptr_dtor_nogc(&result);
2167 }
2168 }
2169
value_from_type_and_range(sccp_ctx * ctx,int var_num,zval * tmp)2170 static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
2171 zend_ssa *ssa = ctx->scdf.ssa;
2172 zend_ssa_var_info *info = &ssa->var_info[var_num];
2173
2174 if (ssa->vars[var_num].var >= ctx->scdf.op_array->last_var) {
2175 // TODO Non-CVs may cause issues with FREEs
2176 return NULL;
2177 }
2178
2179 if (info->type & MAY_BE_UNDEF) {
2180 return NULL;
2181 }
2182
2183 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_NULL))) {
2184 ZVAL_NULL(tmp);
2185 return tmp;
2186 }
2187 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_FALSE))) {
2188 ZVAL_FALSE(tmp);
2189 return tmp;
2190 }
2191 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_TRUE))) {
2192 ZVAL_TRUE(tmp);
2193 return tmp;
2194 }
2195
2196 #if 0
2197 /* Disabled due to bug #81096. */
2198 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))
2199 && info->has_range
2200 && !info->range.overflow && !info->range.underflow
2201 && info->range.min == info->range.max) {
2202 ZVAL_LONG(tmp, info->range.min);
2203 return tmp;
2204 }
2205 #endif
2206
2207 return NULL;
2208 }
2209
2210 /* Call instruction -> remove opcodes that are part of the call */
remove_call(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op)2211 static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op)
2212 {
2213 zend_ssa *ssa = ctx->scdf.ssa;
2214 zend_op_array *op_array = ctx->scdf.op_array;
2215 zend_call_info *call;
2216 int i;
2217
2218 ZEND_ASSERT(ctx->call_map);
2219 call = ctx->call_map[opline - op_array->opcodes];
2220 ZEND_ASSERT(call);
2221 ZEND_ASSERT(call->caller_call_opline == opline);
2222 zend_ssa_remove_instr(ssa, opline, ssa_op);
2223 zend_ssa_remove_instr(ssa, call->caller_init_opline,
2224 &ssa->ops[call->caller_init_opline - op_array->opcodes]);
2225
2226 for (i = 0; i < call->num_args; i++) {
2227 zend_ssa_remove_instr(ssa, call->arg_info[i].opline,
2228 &ssa->ops[call->arg_info[i].opline - op_array->opcodes]);
2229 }
2230
2231 // TODO: remove call_info completely???
2232 call->callee_func = NULL;
2233
2234 return call->num_args + 2;
2235 }
2236
2237 /* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
2238 * value(s) were determined by SCCP. It removes dead computational instructions and converts
2239 * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
2240 * a) During operand replacement we eliminate FREEs. The corresponding computational instructions
2241 * must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
2242 * b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
2243 * and can't be DCEd. This means that it will not be able collect all instructions rendered dead
2244 * by SCCP, because they may have potentially side-effecting types, but the actual values are
2245 * not. As such doing DCE here will allow us to eliminate more dead code in combination.
2246 * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
2247 * we need to collect.
2248 * d) The ordinary DCE pass cannot collect construction of dead non-escaping arrays and objects.
2249 */
try_remove_definition(sccp_ctx * ctx,int var_num,zend_ssa_var * var,zval * value)2250 static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zval *value)
2251 {
2252 zend_ssa *ssa = ctx->scdf.ssa;
2253 zend_op_array *op_array = ctx->scdf.op_array;
2254 int removed_ops = 0;
2255
2256 if (var->definition >= 0) {
2257 zend_op *opline = &op_array->opcodes[var->definition];
2258 zend_ssa_op *ssa_op = &ssa->ops[var->definition];
2259
2260 if (opline->opcode == ZEND_ASSIGN) {
2261 /* Leave assigns to DCE (due to dtor effects) */
2262 return 0;
2263 }
2264
2265 if (ssa_op->result_def == var_num) {
2266 if (ssa_op->op1_def >= 0
2267 || ssa_op->op2_def >= 0) {
2268 /* we cannot remove instruction that defines other variables */
2269 return 0;
2270 } else if (opline->opcode == ZEND_JMPZ_EX
2271 || opline->opcode == ZEND_JMPNZ_EX
2272 || opline->opcode == ZEND_JMP_SET
2273 || opline->opcode == ZEND_COALESCE
2274 || opline->opcode == ZEND_FE_RESET_R
2275 || opline->opcode == ZEND_FE_RESET_RW
2276 || opline->opcode == ZEND_FE_FETCH_R
2277 || opline->opcode == ZEND_FE_FETCH_RW
2278 || opline->opcode == ZEND_NEW) {
2279 /* we cannot simple remove jump instructions */
2280 return 0;
2281 } else if (var->use_chain >= 0
2282 || var->phi_use_chain != NULL) {
2283 if (value
2284 && opline->result_type & (IS_VAR|IS_TMP_VAR)
2285 && opline->opcode != ZEND_QM_ASSIGN
2286 && opline->opcode != ZEND_ROPE_INIT
2287 && opline->opcode != ZEND_ROPE_ADD
2288 && opline->opcode != ZEND_INIT_ARRAY
2289 && opline->opcode != ZEND_ADD_ARRAY_ELEMENT
2290 && opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
2291 /* Replace with QM_ASSIGN */
2292 zend_uchar old_type = opline->result_type;
2293 uint32_t old_var = opline->result.var;
2294
2295 ssa_op->result_def = -1;
2296 if (opline->opcode == ZEND_DO_ICALL) {
2297 removed_ops = remove_call(ctx, opline, ssa_op) - 1;
2298 } else {
2299 zend_ssa_remove_instr(ssa, opline, ssa_op);
2300 }
2301 ssa_op->result_def = var_num;
2302 opline->opcode = ZEND_QM_ASSIGN;
2303 opline->result_type = old_type;
2304 opline->result.var = old_var;
2305 Z_TRY_ADDREF_P(value);
2306 zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, value);
2307 }
2308 return 0;
2309 } else {
2310 zend_ssa_remove_result_def(ssa, ssa_op);
2311 if (opline->opcode == ZEND_DO_ICALL) {
2312 removed_ops = remove_call(ctx, opline, ssa_op);
2313 } else if (opline->opcode == ZEND_TYPE_CHECK
2314 && opline->op1_type & (IS_VAR|IS_TMP_VAR)
2315 && !value_known(&ctx->values[ssa_op->op1_use])) {
2316 /* For TYPE_CHECK we may compute the result value without knowing the
2317 * operand, based on type inference information. Make sure the operand is
2318 * freed and leave further cleanup to DCE. */
2319 opline->opcode = ZEND_FREE;
2320 opline->result_type = IS_UNUSED;
2321 removed_ops++;
2322 } else {
2323 zend_ssa_remove_instr(ssa, opline, ssa_op);
2324 removed_ops++;
2325 }
2326 }
2327 } else if (ssa_op->op1_def == var_num) {
2328 /* Compound assign or incdec -> convert to direct ASSIGN */
2329
2330 if (!value) {
2331 /* In some cases zend_may_throw() may be avoided */
2332 switch (opline->opcode) {
2333 case ZEND_ASSIGN_DIM:
2334 case ZEND_ASSIGN_OBJ:
2335 case ZEND_ASSIGN_OP:
2336 case ZEND_ASSIGN_DIM_OP:
2337 case ZEND_ASSIGN_OBJ_OP:
2338 case ZEND_ASSIGN_STATIC_PROP_OP:
2339 if ((ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use]))
2340 || ((ssa_op+1)->op1_use >= 0 &&!value_known(&ctx->values[(ssa_op+1)->op1_use]))) {
2341 return 0;
2342 }
2343 break;
2344 case ZEND_PRE_INC_OBJ:
2345 case ZEND_PRE_DEC_OBJ:
2346 case ZEND_POST_INC_OBJ:
2347 case ZEND_POST_DEC_OBJ:
2348 if (ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use])) {
2349 return 0;
2350 }
2351 break;
2352 default:
2353 if (zend_may_throw(opline, op_array, ssa)) {
2354 return 0;
2355 }
2356 break;
2357 }
2358 }
2359
2360 /* Mark result unused, if possible */
2361 if (ssa_op->result_def >= 0) {
2362 if (ssa->vars[ssa_op->result_def].use_chain < 0
2363 && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
2364 zend_ssa_remove_result_def(ssa, ssa_op);
2365 opline->result_type = IS_UNUSED;
2366 } else if (opline->opcode != ZEND_PRE_INC &&
2367 opline->opcode != ZEND_PRE_DEC) {
2368 /* op1_def and result_def are different */
2369 return removed_ops;
2370 }
2371 }
2372
2373 /* Destroy previous op2 */
2374 if (opline->op2_type == IS_CONST) {
2375 literal_dtor(&ZEND_OP2_LITERAL(opline));
2376 } else if (ssa_op->op2_use >= 0) {
2377 if (ssa_op->op2_use != ssa_op->op1_use) {
2378 zend_ssa_unlink_use_chain(ssa, var->definition, ssa_op->op2_use);
2379 }
2380 ssa_op->op2_use = -1;
2381 ssa_op->op2_use_chain = -1;
2382 }
2383
2384 /* Remove OP_DATA opcode */
2385 switch (opline->opcode) {
2386 case ZEND_ASSIGN_DIM:
2387 case ZEND_ASSIGN_OBJ:
2388 removed_ops++;
2389 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
2390 break;
2391 case ZEND_ASSIGN_DIM_OP:
2392 case ZEND_ASSIGN_OBJ_OP:
2393 case ZEND_ASSIGN_STATIC_PROP_OP:
2394 removed_ops++;
2395 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
2396 break;
2397 default:
2398 break;
2399 }
2400
2401 if (value) {
2402 /* Convert to ASSIGN */
2403 opline->opcode = ZEND_ASSIGN;
2404 opline->op2_type = IS_CONST;
2405 opline->op2.constant = zend_optimizer_add_literal(op_array, value);
2406 Z_TRY_ADDREF_P(value);
2407 } else {
2408 /* Remove dead array or object construction */
2409 removed_ops++;
2410 if (var->use_chain >= 0 || var->phi_use_chain != NULL) {
2411 zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
2412 }
2413 zend_ssa_remove_op1_def(ssa, ssa_op);
2414 zend_ssa_remove_instr(ssa, opline, ssa_op);
2415 }
2416 }
2417 } else if (var->definition_phi
2418 && var->use_chain < 0
2419 && var->phi_use_chain == NULL) {
2420 zend_ssa_remove_phi(ssa, var->definition_phi);
2421 }
2422 return removed_ops;
2423 }
2424
2425 /* This will try to replace uses of SSA variables we have determined to be constant. Not all uses
2426 * can be replaced, because some instructions don't accept constant operands or only accept them
2427 * if they have a certain type. */
replace_constant_operands(sccp_ctx * ctx)2428 static int replace_constant_operands(sccp_ctx *ctx) {
2429 zend_ssa *ssa = ctx->scdf.ssa;
2430 zend_op_array *op_array = ctx->scdf.op_array;
2431 int i;
2432 zval tmp;
2433 int removed_ops = 0;
2434
2435 /* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
2436 * and INIT_ARRAY. */
2437 for (i = ssa->vars_count - 1; i >= op_array->last_var; i--) {
2438 zend_ssa_var *var = &ssa->vars[i];
2439 zval *value;
2440 int use;
2441
2442 if (IS_PARTIAL_ARRAY(&ctx->values[i])
2443 || IS_PARTIAL_OBJECT(&ctx->values[i])) {
2444 if (!Z_DELREF(ctx->values[i])) {
2445 zend_array_destroy(Z_ARR(ctx->values[i]));
2446 }
2447 MAKE_BOT(&ctx->values[i]);
2448 if ((var->use_chain < 0 && var->phi_use_chain == NULL) || var->no_val) {
2449 removed_ops += try_remove_definition(ctx, i, var, NULL);
2450 }
2451 continue;
2452 } else if (value_known(&ctx->values[i])) {
2453 value = &ctx->values[i];
2454 } else {
2455 value = value_from_type_and_range(ctx, i, &tmp);
2456 if (!value) {
2457 continue;
2458 }
2459 }
2460
2461 FOREACH_USE(var, use) {
2462 zend_op *opline = &op_array->opcodes[use];
2463 zend_ssa_op *ssa_op = &ssa->ops[use];
2464 if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
2465 if (opline->opcode == ZEND_NOP) {
2466 removed_ops++;
2467 }
2468 ZEND_ASSERT(ssa_op->op1_def == -1);
2469 if (ssa_op->op1_use != ssa_op->op2_use) {
2470 zend_ssa_unlink_use_chain(ssa, use, ssa_op->op1_use);
2471 } else {
2472 ssa_op->op2_use_chain = ssa_op->op1_use_chain;
2473 }
2474 ssa_op->op1_use = -1;
2475 ssa_op->op1_use_chain = -1;
2476 }
2477 if (try_replace_op2(ctx, opline, ssa_op, i, value)) {
2478 ZEND_ASSERT(ssa_op->op2_def == -1);
2479 if (ssa_op->op2_use != ssa_op->op1_use) {
2480 zend_ssa_unlink_use_chain(ssa, use, ssa_op->op2_use);
2481 }
2482 ssa_op->op2_use = -1;
2483 ssa_op->op2_use_chain = -1;
2484 }
2485 } FOREACH_USE_END();
2486
2487 if (value_known(&ctx->values[i])) {
2488 removed_ops += try_remove_definition(ctx, i, var, value);
2489 }
2490 }
2491
2492 return removed_ops;
2493 }
2494
sccp_context_init(zend_optimizer_ctx * ctx,sccp_ctx * sccp,zend_ssa * ssa,zend_op_array * op_array,zend_call_info ** call_map)2495 static void sccp_context_init(zend_optimizer_ctx *ctx, sccp_ctx *sccp,
2496 zend_ssa *ssa, zend_op_array *op_array, zend_call_info **call_map) {
2497 int i;
2498 sccp->call_map = call_map;
2499 sccp->values = zend_arena_alloc(&ctx->arena, sizeof(zval) * ssa->vars_count);
2500
2501 MAKE_TOP(&sccp->top);
2502 MAKE_BOT(&sccp->bot);
2503
2504 i = 0;
2505 for (; i < op_array->last_var; ++i) {
2506 /* These are all undefined variables, which we have to mark BOT.
2507 * Otherwise the undefined variable warning might not be preserved. */
2508 MAKE_BOT(&sccp->values[i]);
2509 }
2510 for (; i < ssa->vars_count; ++i) {
2511 if (ssa->vars[i].alias) {
2512 MAKE_BOT(&sccp->values[i]);
2513 } else {
2514 MAKE_TOP(&sccp->values[i]);
2515 }
2516 }
2517 }
2518
sccp_context_free(sccp_ctx * sccp)2519 static void sccp_context_free(sccp_ctx *sccp) {
2520 int i;
2521 for (i = sccp->scdf.op_array->last_var; i < sccp->scdf.ssa->vars_count; ++i) {
2522 zval_ptr_dtor_nogc(&sccp->values[i]);
2523 }
2524 }
2525
sccp_optimize_op_array(zend_optimizer_ctx * ctx,zend_op_array * op_array,zend_ssa * ssa,zend_call_info ** call_map)2526 int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
2527 {
2528 sccp_ctx sccp;
2529 int removed_ops = 0;
2530 void *checkpoint = zend_arena_checkpoint(ctx->arena);
2531
2532 sccp_context_init(ctx, &sccp, ssa, op_array, call_map);
2533
2534 sccp.scdf.handlers.visit_instr = sccp_visit_instr;
2535 sccp.scdf.handlers.visit_phi = sccp_visit_phi;
2536 sccp.scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors;
2537
2538 scdf_init(ctx, &sccp.scdf, op_array, ssa);
2539 scdf_solve(&sccp.scdf, "SCCP");
2540
2541 if (ctx->debug_level & ZEND_DUMP_SCCP) {
2542 int i, first = 1;
2543
2544 for (i = op_array->last_var; i < ssa->vars_count; i++) {
2545 zval *zv = &sccp.values[i];
2546
2547 if (IS_TOP(zv) || IS_BOT(zv)) {
2548 continue;
2549 }
2550 if (first) {
2551 first = 0;
2552 fprintf(stderr, "\nSCCP Values for \"");
2553 zend_dump_op_array_name(op_array);
2554 fprintf(stderr, "\":\n");
2555 }
2556 fprintf(stderr, " #%d.", i);
2557 zend_dump_var(op_array, IS_CV, ssa->vars[i].var);
2558 fprintf(stderr, " =");
2559 scp_dump_value(zv);
2560 fprintf(stderr, "\n");
2561 }
2562 }
2563
2564 removed_ops += scdf_remove_unreachable_blocks(&sccp.scdf);
2565 removed_ops += replace_constant_operands(&sccp);
2566
2567 sccp_context_free(&sccp);
2568 zend_arena_release(&ctx->arena, checkpoint);
2569
2570 return removed_ops;
2571 }
2572