1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine, SCCP - Sparse Conditional Constant Propagation |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 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 +----------------------------------------------------------------------+
17 */
18
19 #include "php.h"
20 #include "zend_type_info.h"
21 #include "ZendAccelerator.h"
22 #include "Optimizer/zend_optimizer_internal.h"
23 #include "Optimizer/zend_call_graph.h"
24 #include "Optimizer/scdf.h"
25 #include "Optimizer/zend_dump.h"
26 #include "ext/standard/php_string.h"
27
28 /* This implements sparse conditional constant propagation (SCCP) based on the SCDF framework. The
29 * used value lattice is defined as follows:
30 *
31 * BOT < {constant values} < TOP
32 *
33 * TOP indicates an underdefined value, i.e. that we do not yet know the value of variable.
34 * BOT indicates an overdefined value, i.e. that we know the variable to be non-constant.
35 *
36 * All variables are optimistically initialized to TOP, apart from the implicit variables defined
37 * at the start of the first block. Note that variables that MAY_BE_REF are *not* initialized to
38 * BOT. We rely on the fact that any operation resulting in a reference will produce a BOT anyway.
39 * This is better because such operations might never be reached due to the conditional nature of
40 * the algorithm.
41 *
42 * The meet operation for phi functions is defined as follows:
43 * BOT + any = BOT
44 * TOP + any = any
45 * C_i + C_i = C_i (i.e. two equal constants)
46 * C_i + C_j = BOT (i.e. two different constants)
47 *
48 * When evaluating instructions TOP and BOT are handled as follows:
49 * a) If any operand is BOT, the result is BOT. The main exception to this is op1 of ASSIGN, which
50 * is ignored. However, if the op1 MAY_BE_REF we do have to propagate the BOT.
51 * b) Otherwise, if the instruction can never be evaluated (either in general, or with the
52 * specific modifiers) the result is BOT.
53 * c) Otherwise, if any operand is TOP, the result is TOP.
54 * d) Otherwise (at this point all operands are known and constant), if we can compute the result
55 * for these specific constants (without throwing notices or similar) then that is the result.
56 * e) Otherwise the result is BOT.
57 *
58 * It is sometimes possible to determine a result even if one argument is TOP / BOT, e.g. for things
59 * like BOT*0. Right now we don't bother with this -- the only thing that is done is evaluating
60 * TYPE_CHECKS based on the type information.
61 *
62 * Feasible successors for conditional branches are determined as follows:
63 * a) If we don't support the branch type or branch on BOT, all successors are feasible.
64 * b) Otherwise, if we branch on TOP none of the successors are feasible.
65 * c) Otherwise (we branch on a constant), the feasible successors are marked based on the constant
66 * (usually only one successor will be feasible).
67 */
68
69 #if 0
70 #define SCP_DEBUG(...) php_printf(__VA_ARGS__)
71 #else
72 #define SCP_DEBUG(...)
73 #endif
74
75 typedef struct _sccp_ctx {
76 scdf_ctx scdf;
77 zend_call_info **call_map;
78 zval *values;
79 zval top;
80 zval bot;
81 } sccp_ctx;
82
83 #define TOP ((zend_uchar)-1)
84 #define BOT ((zend_uchar)-2)
85 #define IS_TOP(zv) (Z_TYPE_P(zv) == TOP)
86 #define IS_BOT(zv) (Z_TYPE_P(zv) == BOT)
87
88 #define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP)
89 #define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT)
90
value_known(zval * zv)91 static inline zend_bool value_known(zval *zv) {
92 return !IS_TOP(zv) && !IS_BOT(zv);
93 }
94
95 /* Sets new value for variable and ensures that it is lower or equal
96 * the previous one in the constant propagation lattice. */
set_value(scdf_ctx * scdf,sccp_ctx * ctx,int var,zval * new)97 static void set_value(scdf_ctx *scdf, sccp_ctx *ctx, int var, zval *new) {
98 zval *value = &ctx->values[var];
99 if (IS_BOT(value) || IS_TOP(new)) {
100 return;
101 }
102
103 if (IS_BOT(new)) {
104 SCP_DEBUG("Lowering var %d to BOT\n", var);
105 } else {
106 SCP_DEBUG("Lowering var %d to %Z\n", var, new);
107 }
108
109 if (IS_TOP(value) || IS_BOT(new)) {
110 zval_ptr_dtor_nogc(value);
111 ZVAL_COPY(value, new);
112 scdf_add_to_worklist(scdf, var);
113 return;
114 }
115
116 #if ZEND_DEBUG
117 ZEND_ASSERT(zend_is_identical(value, new));
118 #endif
119 }
120
get_op1_value(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op)121 static zval *get_op1_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
122 if (opline->op1_type == IS_CONST) {
123 return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op1.constant);
124 } else if (ssa_op->op1_use != -1) {
125 return &ctx->values[ssa_op->op1_use];
126 } else {
127 return NULL;
128 }
129 }
130
get_op2_value(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op)131 static zval *get_op2_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
132 if (opline->op2_type == IS_CONST) {
133 return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op2.constant);
134 } else if (ssa_op->op2_use != -1) {
135 return &ctx->values[ssa_op->op2_use];
136 } else {
137 return NULL;
138 }
139 }
140
can_replace_op1(const zend_op_array * op_array,zend_op * opline,zend_ssa_op * ssa_op)141 static zend_bool can_replace_op1(
142 const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
143 switch (opline->opcode) {
144 case ZEND_PRE_INC:
145 case ZEND_PRE_DEC:
146 case ZEND_PRE_INC_OBJ:
147 case ZEND_PRE_DEC_OBJ:
148 case ZEND_POST_INC:
149 case ZEND_POST_DEC:
150 case ZEND_POST_INC_OBJ:
151 case ZEND_POST_DEC_OBJ:
152 case ZEND_ASSIGN:
153 case ZEND_ASSIGN_REF:
154 case ZEND_ASSIGN_DIM:
155 case ZEND_ASSIGN_OBJ:
156 case ZEND_ASSIGN_ADD:
157 case ZEND_ASSIGN_SUB:
158 case ZEND_ASSIGN_MUL:
159 case ZEND_ASSIGN_DIV:
160 case ZEND_ASSIGN_MOD:
161 case ZEND_ASSIGN_SL:
162 case ZEND_ASSIGN_SR:
163 case ZEND_ASSIGN_CONCAT:
164 case ZEND_ASSIGN_BW_OR:
165 case ZEND_ASSIGN_BW_AND:
166 case ZEND_ASSIGN_BW_XOR:
167 case ZEND_ASSIGN_POW:
168 case ZEND_FETCH_DIM_W:
169 case ZEND_FETCH_DIM_RW:
170 case ZEND_FETCH_DIM_UNSET:
171 case ZEND_FETCH_DIM_FUNC_ARG:
172 case ZEND_FETCH_OBJ_W:
173 case ZEND_FETCH_OBJ_RW:
174 case ZEND_FETCH_OBJ_UNSET:
175 case ZEND_FETCH_OBJ_FUNC_ARG:
176 case ZEND_UNSET_DIM:
177 case ZEND_UNSET_OBJ:
178 case ZEND_SEND_REF:
179 case ZEND_SEND_VAR_EX:
180 case ZEND_SEND_UNPACK:
181 case ZEND_SEND_ARRAY:
182 case ZEND_SEND_USER:
183 case ZEND_FE_RESET_RW:
184 return 0;
185 /* Do not accept CONST */
186 case ZEND_VERIFY_ABSTRACT_CLASS:
187 case ZEND_ADD_INTERFACE:
188 case ZEND_ADD_TRAIT:
189 case ZEND_BIND_TRAITS:
190 case ZEND_ROPE_ADD:
191 case ZEND_ROPE_END:
192 case ZEND_BIND_STATIC:
193 case ZEND_BIND_GLOBAL:
194 case ZEND_MAKE_REF:
195 case ZEND_UNSET_CV:
196 case ZEND_ISSET_ISEMPTY_CV:
197 case ZEND_INSTANCEOF:
198 return 0;
199 case ZEND_INIT_ARRAY:
200 case ZEND_ADD_ARRAY_ELEMENT:
201 return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF);
202 case ZEND_YIELD:
203 return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE);
204 case ZEND_VERIFY_RETURN_TYPE:
205 // TODO: This would require a non-local change ???
206 return 0;
207 default:
208 if (ssa_op->op1_def != -1) {
209 ZEND_ASSERT(0);
210 return 0;
211 }
212 }
213
214 return 1;
215 }
216
can_replace_op2(const zend_op_array * op_array,zend_op * opline,zend_ssa_op * ssa_op)217 static zend_bool can_replace_op2(
218 const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
219 switch (opline->opcode) {
220 /* Do not accept CONST */
221 case ZEND_DECLARE_INHERITED_CLASS:
222 case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
223 case ZEND_DECLARE_ANON_INHERITED_CLASS:
224 case ZEND_BIND_LEXICAL:
225 case ZEND_FE_FETCH_R:
226 case ZEND_FE_FETCH_RW:
227 return 0;
228 }
229 return 1;
230 }
231
try_replace_op1(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op,int var,zval * value)232 static zend_bool try_replace_op1(
233 sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
234 if (ssa_op->op1_use == var && can_replace_op1(ctx->scdf.op_array, opline, ssa_op)) {
235 zval zv;
236 ZVAL_COPY(&zv, value);
237 if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) {
238 /* Reconstruct SSA */
239 int num;
240 zend_basic_block *block;
241
242 switch (opline->opcode) {
243 case ZEND_JMPZ:
244 if (zend_is_true(&zv)) {
245 MAKE_NOP(opline);
246 num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
247 block = &ctx->scdf.ssa->cfg.blocks[num];
248 if (block->successors_count == 2) {
249 if (block->successors[1] != block->successors[0]) {
250 zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
251 }
252 block->successors_count = 1;
253 block->successors[0] = block->successors[1];
254 }
255 } else {
256 opline->opcode = ZEND_JMP;
257 COPY_NODE(opline->op1, opline->op2);
258 num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
259 block = &ctx->scdf.ssa->cfg.blocks[num];
260 if (block->successors_count == 2) {
261 if (block->successors[1] != block->successors[0]) {
262 zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
263 }
264 block->successors_count = 1;
265 }
266 }
267 break;
268 case ZEND_JMPNZ:
269 if (zend_is_true(&zv)) {
270 opline->opcode = ZEND_JMP;
271 COPY_NODE(opline->op1, opline->op2);
272 num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
273 block = &ctx->scdf.ssa->cfg.blocks[num];
274 if (block->successors_count == 2) {
275 if (block->successors[1] != block->successors[0]) {
276 zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
277 }
278 block->successors_count = 1;
279 }
280 } else {
281 MAKE_NOP(opline);
282 num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
283 block = &ctx->scdf.ssa->cfg.blocks[num];
284 if (block->successors_count == 2) {
285 if (block->successors[1] != block->successors[0]) {
286 zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
287 }
288 block->successors_count = 1;
289 block->successors[0] = block->successors[1];
290 }
291 }
292 break;
293 case ZEND_JMPZNZ:
294 if (zend_is_true(&zv)) {
295 zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
296 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
297 num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
298 block = &ctx->scdf.ssa->cfg.blocks[num];
299 if (block->successors_count == 2) {
300 if (block->successors[1] != block->successors[0]) {
301 zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
302 }
303 block->successors_count = 1;
304 block->successors[0] = block->successors[1];
305 }
306 } else {
307 zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
308 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
309 num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
310 block = &ctx->scdf.ssa->cfg.blocks[num];
311 if (block->successors_count == 2) {
312 if (block->successors[1] != block->successors[0]) {
313 zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
314 }
315 block->successors_count = 1;
316 }
317 }
318 opline->op1_type = IS_UNUSED;
319 opline->extended_value = 0;
320 opline->opcode = ZEND_JMP;
321 break;
322 default:
323 break;
324 }
325 return 1;
326 } else {
327 // TODO: check the following special cases ???
328 switch (opline->opcode) {
329 case ZEND_FETCH_LIST:
330 case ZEND_CASE:
331 case ZEND_SWITCH_STRING:
332 case ZEND_SWITCH_LONG:
333 if (Z_TYPE(zv) == IS_STRING) {
334 zend_string_hash_val(Z_STR(zv));
335 }
336 opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv);
337 opline->op1_type = IS_CONST;
338 return 1;
339 }
340 zval_ptr_dtor_nogc(&zv);
341 }
342 }
343 return 0;
344 }
345
try_replace_op2(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op,int var,zval * value)346 static zend_bool try_replace_op2(
347 sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
348 if (ssa_op->op2_use == var && can_replace_op2(ctx->scdf.op_array, opline, ssa_op)) {
349 zval zv;
350 ZVAL_COPY(&zv, value);
351 if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline, &zv)) {
352 return 1;
353 } else {
354 zval_ptr_dtor_nogc(&zv);
355 }
356 }
357 return 0;
358 }
359
zval_to_string_offset(zend_long * result,zval * op)360 static inline int zval_to_string_offset(zend_long *result, zval *op) {
361 switch (Z_TYPE_P(op)) {
362 case IS_LONG:
363 *result = Z_LVAL_P(op);
364 return SUCCESS;
365 case IS_STRING:
366 if (IS_LONG == is_numeric_string(
367 Z_STRVAL_P(op), Z_STRLEN_P(op), result, NULL, 0)) {
368 return SUCCESS;
369 }
370 return FAILURE;
371 default:
372 return FAILURE;
373 }
374 }
375
fetch_array_elem(zval ** result,zval * op1,zval * op2)376 static inline int fetch_array_elem(zval **result, zval *op1, zval *op2) {
377 switch (Z_TYPE_P(op2)) {
378 case IS_NULL:
379 *result = zend_hash_find(Z_ARR_P(op1), ZSTR_EMPTY_ALLOC());
380 return SUCCESS;
381 case IS_FALSE:
382 *result = zend_hash_index_find(Z_ARR_P(op1), 0);
383 return SUCCESS;
384 case IS_TRUE:
385 *result = zend_hash_index_find(Z_ARR_P(op1), 1);
386 return SUCCESS;
387 case IS_LONG:
388 *result = zend_hash_index_find(Z_ARR_P(op1), Z_LVAL_P(op2));
389 return SUCCESS;
390 case IS_DOUBLE:
391 *result = zend_hash_index_find(Z_ARR_P(op1), zend_dval_to_lval(Z_DVAL_P(op2)));
392 return SUCCESS;
393 case IS_STRING:
394 *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
395 return SUCCESS;
396 default:
397 return FAILURE;
398 }
399 }
400
ct_eval_fetch_dim(zval * result,zval * op1,zval * op2,int support_strings)401 static inline int ct_eval_fetch_dim(zval *result, zval *op1, zval *op2, int support_strings) {
402 if (Z_TYPE_P(op1) == IS_ARRAY) {
403 zval *value;
404 if (fetch_array_elem(&value, op1, op2) == SUCCESS && value) {
405 ZVAL_COPY(result, value);
406 return SUCCESS;
407 }
408 } else if (support_strings && Z_TYPE_P(op1) == IS_STRING) {
409 zend_long index;
410 if (zval_to_string_offset(&index, op2) == FAILURE) {
411 return FAILURE;
412 }
413 if (index >= 0 && index < Z_STRLEN_P(op1)) {
414 ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0));
415 return SUCCESS;
416 }
417 }
418 return FAILURE;
419 }
420
ct_eval_isset_dim(zval * result,uint32_t extended_value,zval * op1,zval * op2)421 static inline int ct_eval_isset_dim(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
422 if (Z_TYPE_P(op1) == IS_ARRAY) {
423 zval *value;
424 if (fetch_array_elem(&value, op1, op2) == FAILURE) {
425 return FAILURE;
426 }
427 if (extended_value & ZEND_ISSET) {
428 ZVAL_BOOL(result, value && Z_TYPE_P(value) != IS_NULL);
429 } else {
430 ZEND_ASSERT(extended_value & ZEND_ISEMPTY);
431 ZVAL_BOOL(result, !value || !zend_is_true(value));
432 }
433 return SUCCESS;
434 } else if (Z_TYPE_P(op1) == IS_STRING) {
435 // TODO
436 return FAILURE;
437 } else {
438 ZVAL_BOOL(result, extended_value != ZEND_ISSET);
439 return SUCCESS;
440 }
441 }
442
ct_eval_add_array_elem(zval * result,zval * value,zval * key)443 static inline int ct_eval_add_array_elem(zval *result, zval *value, zval *key) {
444 if (!key) {
445 if ((value = zend_hash_next_index_insert(Z_ARR_P(result), value))) {
446 Z_TRY_ADDREF_P(value);
447 return SUCCESS;
448 }
449 return FAILURE;
450 }
451
452 switch (Z_TYPE_P(key)) {
453 case IS_NULL:
454 value = zend_hash_update(Z_ARR_P(result), ZSTR_EMPTY_ALLOC(), value);
455 break;
456 case IS_FALSE:
457 value = zend_hash_index_update(Z_ARR_P(result), 0, value);
458 break;
459 case IS_TRUE:
460 value = zend_hash_index_update(Z_ARR_P(result), 1, value);
461 break;
462 case IS_LONG:
463 value = zend_hash_index_update(Z_ARR_P(result), Z_LVAL_P(key), value);
464 break;
465 case IS_DOUBLE:
466 value = zend_hash_index_update(
467 Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)), value);
468 break;
469 case IS_STRING:
470 value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
471 break;
472 default:
473 return FAILURE;
474 }
475
476 Z_TRY_ADDREF_P(value);
477 return SUCCESS;
478 }
479
ct_eval_assign_dim(zval * result,zval * value,zval * key)480 static inline int ct_eval_assign_dim(zval *result, zval *value, zval *key) {
481 switch (Z_TYPE_P(result)) {
482 case IS_NULL:
483 case IS_FALSE:
484 array_init(result);
485 /* break missing intentionally */
486 case IS_ARRAY:
487 return ct_eval_add_array_elem(result, value, key);
488 case IS_STRING:
489 // TODO Before enabling this case, make sure ARRAY_DIM result op is correct
490 #if 0
491 zend_long index;
492 zend_string *new_str, *value_str;
493 if (!key || Z_TYPE_P(value) == IS_ARRAY
494 || zval_to_string_offset(&index, key) == FAILURE || index < 0) {
495 return FAILURE;
496 }
497
498 if (index >= Z_STRLEN_P(result)) {
499 new_str = zend_string_alloc(index + 1, 0);
500 memcpy(ZSTR_VAL(new_str), Z_STRVAL_P(result), Z_STRLEN_P(result));
501 memset(ZSTR_VAL(new_str) + Z_STRLEN_P(result), ' ', index - Z_STRLEN_P(result));
502 ZSTR_VAL(new_str)[index + 1] = 0;
503 } else {
504 new_str = zend_string_init(Z_STRVAL_P(result), Z_STRLEN_P(result), 0);
505 }
506
507 value_str = zval_get_string(value);
508 ZVAL_STR(result, new_str);
509 Z_STRVAL_P(result)[index] = ZSTR_VAL(value_str)[0];
510 zend_string_release(value_str);
511 #endif
512 return FAILURE;
513 default:
514 return FAILURE;
515 }
516 }
517
ct_eval_incdec(zval * result,zend_uchar opcode,zval * op1)518 static inline int ct_eval_incdec(zval *result, zend_uchar opcode, zval *op1) {
519 ZVAL_COPY(result, op1);
520 if (opcode == ZEND_PRE_INC || opcode == ZEND_POST_INC) {
521 increment_function(result);
522 } else {
523 decrement_function(result);
524 }
525 return SUCCESS;
526 }
527
ct_eval_isset_isempty(zval * result,uint32_t extended_value,zval * op1)528 static inline int ct_eval_isset_isempty(zval *result, uint32_t extended_value, zval *op1) {
529 if (extended_value & ZEND_ISSET) {
530 ZVAL_BOOL(result, Z_TYPE_P(op1) != IS_NULL);
531 } else {
532 ZEND_ASSERT(extended_value & ZEND_ISEMPTY);
533 ZVAL_BOOL(result, !zend_is_true(op1));
534 }
535 return SUCCESS;
536 }
537
ct_eval_type_check(zval * result,uint32_t type,zval * op1)538 static inline void ct_eval_type_check(zval *result, uint32_t type, zval *op1) {
539 if (type == _IS_BOOL) {
540 ZVAL_BOOL(result, Z_TYPE_P(op1) == IS_TRUE || Z_TYPE_P(op1) == IS_FALSE);
541 } else {
542 ZVAL_BOOL(result, Z_TYPE_P(op1) == type);
543 }
544 }
545
ct_eval_in_array(zval * result,uint32_t extended_value,zval * op1,zval * op2)546 static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
547 HashTable *ht;
548 zend_bool res;
549
550 if (Z_TYPE_P(op2) != IS_ARRAY) {
551 return FAILURE;
552 }
553 ht = Z_ARRVAL_P(op2);
554 if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
555 res = zend_hash_exists(ht, Z_STR_P(op1));
556 } else if (extended_value) {
557 if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
558 res = zend_hash_index_exists(ht, Z_LVAL_P(op1));
559 } else {
560 res = 0;
561 }
562 } else if (Z_TYPE_P(op1) <= IS_FALSE) {
563 res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC());
564 } else {
565 zend_string *key;
566 zval key_tmp, result_tmp;
567
568 res = 0;
569 ZEND_HASH_FOREACH_STR_KEY(ht, key) {
570 ZVAL_STR(&key_tmp, key);
571 compare_function(&result_tmp, op1, &key_tmp);
572 if (Z_LVAL(result_tmp) == 0) {
573 res = 1;
574 break;
575 }
576 } ZEND_HASH_FOREACH_END();
577 }
578 ZVAL_BOOL(result, res);
579 return SUCCESS;
580 }
581
582 /* The functions chosen here are simple to implement and either likely to affect a branch,
583 * or just happened to be commonly used with constant operands in WP (need to test other
584 * applications as well, of course). */
ct_eval_func_call(zval * result,zend_string * name,uint32_t num_args,zval ** args)585 static inline int ct_eval_func_call(
586 zval *result, zend_string *name, uint32_t num_args, zval **args) {
587 uint32_t i;
588 zend_execute_data *execute_data, *prev_execute_data;
589 zend_function *func;
590 int overflow;
591
592 if (num_args == 0) {
593 if (zend_string_equals_literal(name, "get_magic_quotes_gpc")
594 || zend_string_equals_literal(name, "get_magic_quotes_gpc_runtime")
595 || zend_string_equals_literal(name, "php_sapi_name")
596 || zend_string_equals_literal(name, "imagetypes")
597 || zend_string_equals_literal(name, "phpversion")) {
598 /* pass */
599 } else {
600 return FAILURE;
601 }
602 } else if (num_args == 1) {
603 if (zend_string_equals_literal(name, "chr")) {
604 zend_long c;
605 if (Z_TYPE_P(args[0]) != IS_LONG) {
606 return FAILURE;
607 }
608
609 c = Z_LVAL_P(args[0]) & 0xff;
610 ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
611 return SUCCESS;
612 } else if (zend_string_equals_literal(name, "count")) {
613 if (Z_TYPE_P(args[0]) != IS_ARRAY) {
614 return FAILURE;
615 }
616
617 ZVAL_LONG(result, zend_hash_num_elements(Z_ARRVAL_P(args[0])));
618 return SUCCESS;
619 } else if (zend_string_equals_literal(name, "ini_get")) {
620 zend_ini_entry *ini_entry;
621
622 if (Z_TYPE_P(args[0]) != IS_STRING) {
623 return FAILURE;
624 }
625
626 ini_entry = zend_hash_find_ptr(EG(ini_directives), Z_STR_P(args[0]));
627 if (!ini_entry) {
628 ZVAL_FALSE(result);
629 } else if (ini_entry->modifiable != ZEND_INI_SYSTEM) {
630 return FAILURE;
631 } else if (ini_entry->value) {
632 ZVAL_STR_COPY(result, ini_entry->value);
633 } else {
634 ZVAL_EMPTY_STRING(result);
635 }
636 return SUCCESS;
637 } else if (zend_string_equals_literal(name, "trim")
638 || zend_string_equals_literal(name, "rtrim")
639 || zend_string_equals_literal(name, "ltrim")
640 || zend_string_equals_literal(name, "str_split")
641 || zend_string_equals_literal(name, "preg_quote")
642 || zend_string_equals_literal(name, "base64_encode")
643 || zend_string_equals_literal(name, "base64_decode")
644 || zend_string_equals_literal(name, "urlencode")
645 || zend_string_equals_literal(name, "urldecode")
646 || zend_string_equals_literal(name, "rawurlencode")
647 || zend_string_equals_literal(name, "rawurldecode")
648 || zend_string_equals_literal(name, "php_uname")) {
649 if (Z_TYPE_P(args[0]) != IS_STRING) {
650 return FAILURE;
651 }
652 /* pass */
653 } else if (zend_string_equals_literal(name, "array_keys")
654 || zend_string_equals_literal(name, "array_values")) {
655 if (Z_TYPE_P(args[0]) != IS_ARRAY) {
656 return FAILURE;
657 }
658 /* pass */
659 } else if (zend_string_equals_literal(name, "array_flip")) {
660 zval *entry;
661
662 if (Z_TYPE_P(args[0]) != IS_ARRAY) {
663 return FAILURE;
664 }
665 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
666 if (Z_TYPE_P(entry) != IS_LONG && Z_TYPE_P(entry) != IS_STRING) {
667 return FAILURE;
668 }
669 } ZEND_HASH_FOREACH_END();
670 /* pass */
671 } else if (zend_string_equals_literal(name, "implode")) {
672 zval *entry;
673
674 if (Z_TYPE_P(args[0]) != IS_ARRAY) {
675 return FAILURE;
676 }
677
678 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
679 if (Z_TYPE_P(entry) > IS_STRING) {
680 return FAILURE;
681 }
682 } ZEND_HASH_FOREACH_END();
683 /* pass */
684 } else if (zend_string_equals_literal(name, "serialize")) {
685 /* pass */
686 } else {
687 return FAILURE;
688 }
689 } else if (num_args == 2) {
690 if (zend_string_equals_literal(name, "in_array")) {
691 if (Z_TYPE_P(args[1]) != IS_ARRAY) {
692 return FAILURE;
693 }
694 /* pass */
695 } else if (zend_string_equals_literal(name, "strpos")) {
696 if (Z_TYPE_P(args[0]) != IS_STRING
697 || Z_TYPE_P(args[1]) != IS_STRING
698 || !Z_STRLEN_P(args[1])
699 || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
700 return FAILURE;
701 }
702 /* pass */
703 } else if (zend_string_equals_literal(name, "str_split")) {
704 if (Z_TYPE_P(args[0]) != IS_STRING
705 || Z_TYPE_P(args[1]) != IS_LONG
706 || Z_LVAL_P(args[1]) <= 0) {
707 return FAILURE;
708 }
709 /* pass */
710 } else if (zend_string_equals_literal(name, "array_key_exists")) {
711 if (Z_TYPE_P(args[1]) != IS_ARRAY
712 || (Z_TYPE_P(args[0]) != IS_LONG
713 && Z_TYPE_P(args[0]) != IS_STRING
714 && Z_TYPE_P(args[0]) != IS_NULL)) {
715 return FAILURE;
716 }
717 /* pass */
718 } else if (zend_string_equals_literal(name, "trim")
719 || zend_string_equals_literal(name, "rtrim")
720 || zend_string_equals_literal(name, "ltrim")
721 || zend_string_equals_literal(name, "preg_quote")) {
722 if (Z_TYPE_P(args[0]) != IS_STRING
723 || Z_TYPE_P(args[1]) != IS_STRING) {
724 return FAILURE;
725 }
726 /* pass */
727 } else if (zend_string_equals_literal(name, "str_repeat")) {
728 if (Z_TYPE_P(args[0]) != IS_STRING
729 || Z_TYPE_P(args[1]) != IS_LONG
730 || zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) > 64 * 1024
731 || overflow) {
732 return FAILURE;
733 }
734 /* pass */
735 } else if (zend_string_equals_literal(name, "array_merge")
736 || zend_string_equals_literal(name, "array_replace")
737 || zend_string_equals_literal(name, "array_merge_recursive")
738 || zend_string_equals_literal(name, "array_replace_recursive")
739 || zend_string_equals_literal(name, "array_diff")
740 || zend_string_equals_literal(name, "array_diff_assoc")
741 || zend_string_equals_literal(name, "array_diff_key")) {
742 for (i = 0; i < num_args; i++) {
743 if (Z_TYPE_P(args[i]) != IS_ARRAY) {
744 return FAILURE;
745 }
746 }
747 /* pass */
748 } else if (zend_string_equals_literal(name, "implode")) {
749 zval *entry;
750
751 if ((Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_ARRAY)
752 && (Z_TYPE_P(args[0]) != IS_ARRAY || Z_TYPE_P(args[1]) != IS_STRING)) {
753 return FAILURE;
754 }
755
756 if (Z_TYPE_P(args[0]) == IS_ARRAY) {
757 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
758 if (Z_TYPE_P(entry) > IS_STRING) {
759 return FAILURE;
760 }
761 } ZEND_HASH_FOREACH_END();
762 } else {
763 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[1]), entry) {
764 if (Z_TYPE_P(entry) > IS_STRING) {
765 return FAILURE;
766 }
767 } ZEND_HASH_FOREACH_END();
768 }
769 /* pass */
770 } else if (zend_string_equals_literal(name, "version_compare")) {
771 if (Z_TYPE_P(args[0]) != IS_STRING
772 || Z_TYPE_P(args[1]) != IS_STRING) {
773 return FAILURE;
774 }
775 /* pass */
776 } else if (zend_string_equals_literal(name, "substr")) {
777 if (Z_TYPE_P(args[0]) != IS_STRING
778 || Z_TYPE_P(args[1]) != IS_LONG
779 || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
780 return FAILURE;
781 }
782 /* pass */
783 } else if (zend_string_equals_literal(name, "pow")) {
784 if ((Z_TYPE_P(args[0]) != IS_LONG && Z_TYPE_P(args[0]) != IS_DOUBLE)
785 || (Z_TYPE_P(args[1]) != IS_LONG && Z_TYPE_P(args[1]) != IS_DOUBLE)) {
786 return FAILURE;
787 }
788 /* pass */
789 } else {
790 return FAILURE;
791 }
792 } else if (num_args == 3) {
793 if (zend_string_equals_literal(name, "in_array")) {
794 if (Z_TYPE_P(args[1]) != IS_ARRAY
795 || (Z_TYPE_P(args[2]) != IS_FALSE
796 && Z_TYPE_P(args[2]) != IS_TRUE)) {
797 return FAILURE;
798 }
799 /* pass */
800 } else if (zend_string_equals_literal(name, "array_merge")
801 || zend_string_equals_literal(name, "array_replace")
802 || zend_string_equals_literal(name, "array_merge_recursive")
803 || zend_string_equals_literal(name, "array_replace_recursive")
804 || zend_string_equals_literal(name, "array_diff")
805 || zend_string_equals_literal(name, "array_diff_assoc")
806 || zend_string_equals_literal(name, "array_diff_key")) {
807 for (i = 0; i < num_args; i++) {
808 if (Z_TYPE_P(args[i]) != IS_ARRAY) {
809 return FAILURE;
810 }
811 }
812 /* pass */
813 } else if (zend_string_equals_literal(name, "version_compare")) {
814 if (Z_TYPE_P(args[0]) != IS_STRING
815 || Z_TYPE_P(args[1]) != IS_STRING
816 || Z_TYPE_P(args[2]) != IS_STRING) {
817 return FAILURE;
818 }
819 /* pass */
820 } else if (zend_string_equals_literal(name, "substr")) {
821 if (Z_TYPE_P(args[0]) != IS_STRING
822 || Z_TYPE_P(args[1]) != IS_LONG
823 || Z_TYPE_P(args[2]) != IS_LONG
824 || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
825 return FAILURE;
826 }
827 /* pass */
828 } else {
829 return FAILURE;
830 }
831 } else {
832 return FAILURE;
833 }
834
835 func = zend_hash_find_ptr(CG(function_table), name);
836 if (!func || func->type != ZEND_INTERNAL_FUNCTION
837 || func->internal_function.handler == ZEND_FN(display_disabled_function)) {
838 return FAILURE;
839 }
840
841 execute_data = safe_emalloc(num_args, sizeof(zval), ZEND_CALL_FRAME_SLOT * sizeof(zval));
842 memset(execute_data, 0, sizeof(zend_execute_data));
843 prev_execute_data = EG(current_execute_data);
844 EG(current_execute_data) = execute_data;
845
846 EX(func) = func;
847 EX_NUM_ARGS() = num_args;
848 for (i = 0; i < num_args; i++) {
849 ZVAL_COPY(EX_VAR_NUM(i), args[i]);
850 }
851 func->internal_function.handler(execute_data, result);
852 for (i = 0; i < num_args; i++) {
853 zval_ptr_dtor_nogc(EX_VAR_NUM(i));
854 }
855 efree(execute_data);
856 EG(current_execute_data) = prev_execute_data;
857 return SUCCESS;
858 }
859
860 #define SET_RESULT(op, zv) do { \
861 if (ssa_op->op##_def >= 0) { \
862 set_value(scdf, ctx, ssa_op->op##_def, zv); \
863 } \
864 } while (0)
865 #define SET_RESULT_BOT(op) SET_RESULT(op, &ctx->bot)
866 #define SET_RESULT_TOP(op) SET_RESULT(op, &ctx->top)
867
868 #define SKIP_IF_TOP(op) if (IS_TOP(op)) break;
869
sccp_visit_instr(scdf_ctx * scdf,zend_op * opline,zend_ssa_op * ssa_op)870 static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) {
871 sccp_ctx *ctx = (sccp_ctx *) scdf;
872 zval *op1, *op2, zv; /* zv is a temporary to hold result values */
873
874 op1 = get_op1_value(ctx, opline, ssa_op);
875 op2 = get_op2_value(ctx, opline, ssa_op);
876
877 switch (opline->opcode) {
878 case ZEND_ASSIGN:
879 /* The value of op1 is irrelevant here, because we are overwriting it
880 * -- unless it can be a reference, in which case we propagate a BOT. */
881 if (IS_BOT(op1) && (ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF)) {
882 SET_RESULT_BOT(op1);
883 } else {
884 SET_RESULT(op1, op2);
885 }
886
887 SET_RESULT(result, op2);
888 return;
889 case ZEND_TYPE_CHECK:
890 /* We may be able to evaluate TYPE_CHECK based on type inference info,
891 * even if we don't know the precise value. */
892 if (!value_known(op1)) {
893 uint32_t type = ctx->scdf.ssa->var_info[ssa_op->op1_use].type;
894 uint32_t expected_type = opline->extended_value == _IS_BOOL
895 ? (MAY_BE_TRUE|MAY_BE_FALSE) : (1 << opline->extended_value);
896 if (!(type & expected_type) && !(type & MAY_BE_UNDEF)) {
897 ZVAL_FALSE(&zv);
898 SET_RESULT(result, &zv);
899 return;
900 } else if (!(type & ((MAY_BE_ANY|MAY_BE_UNDEF) - expected_type))
901 && opline->extended_value != IS_RESOURCE) {
902 ZVAL_TRUE(&zv);
903 SET_RESULT(result, &zv);
904 return;
905 }
906 }
907 break;
908 case ZEND_ASSIGN_DIM:
909 /* If $a in $a[$b]=$c is UNDEF, treat it like NULL. There is no warning. */
910 if ((ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_ANY) == 0) {
911 op1 = &EG(uninitialized_zval);
912 }
913 break;
914 case ZEND_SEND_VAL:
915 case ZEND_SEND_VAR:
916 {
917 /* If the value of a SEND for an ICALL changes, we need to reconsider the
918 * ICALL result value. Otherwise we can ignore the opcode. */
919 zend_call_info *call;
920 if (!ctx->call_map) {
921 return;
922 }
923
924 call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
925 if (IS_TOP(op1) || !call || call->caller_call_opline->opcode != ZEND_DO_ICALL) {
926 return;
927 }
928
929 opline = call->caller_call_opline;
930 ssa_op = &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes];
931 break;
932 }
933 }
934
935 if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) {
936 /* If any operand is BOT, mark the result as BOT right away.
937 * Exceptions to this rule are handled above. */
938 SET_RESULT_BOT(result);
939 SET_RESULT_BOT(op1);
940 SET_RESULT_BOT(op2);
941 return;
942 }
943
944 switch (opline->opcode) {
945 case ZEND_ADD:
946 case ZEND_SUB:
947 case ZEND_MUL:
948 case ZEND_DIV:
949 case ZEND_MOD:
950 case ZEND_POW:
951 case ZEND_SL:
952 case ZEND_SR:
953 case ZEND_CONCAT:
954 case ZEND_FAST_CONCAT:
955 case ZEND_IS_EQUAL:
956 case ZEND_IS_NOT_EQUAL:
957 case ZEND_IS_SMALLER:
958 case ZEND_IS_SMALLER_OR_EQUAL:
959 case ZEND_IS_IDENTICAL:
960 case ZEND_IS_NOT_IDENTICAL:
961 case ZEND_BW_OR:
962 case ZEND_BW_AND:
963 case ZEND_BW_XOR:
964 case ZEND_BOOL_XOR:
965 case ZEND_CASE:
966 SKIP_IF_TOP(op1);
967 SKIP_IF_TOP(op2);
968
969 if (zend_optimizer_eval_binary_op(&zv, opline->opcode, op1, op2) == SUCCESS) {
970 SET_RESULT(result, &zv);
971 zval_ptr_dtor_nogc(&zv);
972 break;
973 }
974 SET_RESULT_BOT(result);
975 break;
976 case ZEND_ASSIGN_ADD:
977 case ZEND_ASSIGN_SUB:
978 case ZEND_ASSIGN_MUL:
979 case ZEND_ASSIGN_DIV:
980 case ZEND_ASSIGN_MOD:
981 case ZEND_ASSIGN_SL:
982 case ZEND_ASSIGN_SR:
983 case ZEND_ASSIGN_CONCAT:
984 case ZEND_ASSIGN_BW_OR:
985 case ZEND_ASSIGN_BW_AND:
986 case ZEND_ASSIGN_BW_XOR:
987 case ZEND_ASSIGN_POW:
988 /* Obj/dim compound assign */
989 if (opline->extended_value) {
990 SET_RESULT_BOT(op1);
991 SET_RESULT_BOT(result);
992 break;
993 }
994
995 SKIP_IF_TOP(op1);
996 SKIP_IF_TOP(op2);
997
998 if (zend_optimizer_eval_binary_op(&zv, zend_compound_assign_to_binary_op(opline->opcode), op1, op2) == SUCCESS) {
999 SET_RESULT(op1, &zv);
1000 SET_RESULT(result, &zv);
1001 zval_ptr_dtor_nogc(&zv);
1002 break;
1003 }
1004 SET_RESULT_BOT(op1);
1005 SET_RESULT_BOT(result);
1006 break;
1007 case ZEND_PRE_INC:
1008 case ZEND_PRE_DEC:
1009 SKIP_IF_TOP(op1);
1010 if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1011 SET_RESULT(op1, &zv);
1012 SET_RESULT(result, &zv);
1013 zval_ptr_dtor_nogc(&zv);
1014 break;
1015 }
1016 SET_RESULT_BOT(op1);
1017 SET_RESULT_BOT(result);
1018 break;
1019 case ZEND_POST_INC:
1020 case ZEND_POST_DEC:
1021 SKIP_IF_TOP(op1);
1022 SET_RESULT(result, op1);
1023 if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1024 SET_RESULT(op1, &zv);
1025 zval_ptr_dtor_nogc(&zv);
1026 break;
1027 }
1028 SET_RESULT_BOT(op1);
1029 break;
1030 case ZEND_BW_NOT:
1031 case ZEND_BOOL_NOT:
1032 SKIP_IF_TOP(op1);
1033 if (zend_optimizer_eval_unary_op(&zv, opline->opcode, op1) == SUCCESS) {
1034 SET_RESULT(result, &zv);
1035 zval_ptr_dtor_nogc(&zv);
1036 break;
1037 }
1038 SET_RESULT_BOT(result);
1039 break;
1040 case ZEND_CAST:
1041 SKIP_IF_TOP(op1);
1042 if (zend_optimizer_eval_cast(&zv, opline->extended_value, op1) == SUCCESS) {
1043 SET_RESULT(result, &zv);
1044 zval_ptr_dtor_nogc(&zv);
1045 break;
1046 }
1047 SET_RESULT_BOT(result);
1048 break;
1049 case ZEND_BOOL:
1050 case ZEND_JMPZ_EX:
1051 case ZEND_JMPNZ_EX:
1052 SKIP_IF_TOP(op1);
1053 ZVAL_BOOL(&zv, zend_is_true(op1));
1054 SET_RESULT(result, &zv);
1055 break;
1056 case ZEND_STRLEN:
1057 SKIP_IF_TOP(op1);
1058 if (zend_optimizer_eval_strlen(&zv, op1) == SUCCESS) {
1059 SET_RESULT(result, &zv);
1060 zval_ptr_dtor_nogc(&zv);
1061 break;
1062 }
1063 SET_RESULT_BOT(result);
1064 break;
1065 case ZEND_COUNT:
1066 SKIP_IF_TOP(op1);
1067 if (Z_TYPE_P(op1) == IS_ARRAY) {
1068 ZVAL_LONG(&zv, zend_hash_num_elements(Z_ARRVAL_P(op1)));
1069 SET_RESULT(result, &zv);
1070 zval_ptr_dtor_nogc(&zv);
1071 break;
1072 }
1073 SET_RESULT_BOT(result);
1074 break;
1075 case ZEND_IN_ARRAY:
1076 SKIP_IF_TOP(op1);
1077 SKIP_IF_TOP(op2);
1078 if (ct_eval_in_array(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1079 SET_RESULT(result, &zv);
1080 zval_ptr_dtor_nogc(&zv);
1081 break;
1082 }
1083 SET_RESULT_BOT(result);
1084 break;
1085 case ZEND_FETCH_DIM_R:
1086 case ZEND_FETCH_DIM_IS:
1087 case ZEND_FETCH_LIST:
1088 SKIP_IF_TOP(op1);
1089 SKIP_IF_TOP(op2);
1090
1091 if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST)) == SUCCESS) {
1092 SET_RESULT(result, &zv);
1093 zval_ptr_dtor_nogc(&zv);
1094 break;
1095 }
1096 SET_RESULT_BOT(result);
1097 break;
1098 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
1099 SKIP_IF_TOP(op1);
1100 SKIP_IF_TOP(op2);
1101
1102 if (ct_eval_isset_dim(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1103 SET_RESULT(result, &zv);
1104 zval_ptr_dtor_nogc(&zv);
1105 break;
1106 }
1107 SET_RESULT_BOT(result);
1108 break;
1109 case ZEND_QM_ASSIGN:
1110 case ZEND_JMP_SET:
1111 case ZEND_COALESCE:
1112 SET_RESULT(result, op1);
1113 break;
1114 case ZEND_FETCH_CLASS:
1115 if (!op1) {
1116 SET_RESULT_BOT(result);
1117 break;
1118 }
1119 SET_RESULT(result, op1);
1120 break;
1121 case ZEND_ISSET_ISEMPTY_CV:
1122 SKIP_IF_TOP(op1);
1123 if (ct_eval_isset_isempty(&zv, opline->extended_value, op1) == SUCCESS) {
1124 SET_RESULT(result, &zv);
1125 zval_ptr_dtor_nogc(&zv);
1126 break;
1127 }
1128 SET_RESULT_BOT(result);
1129 break;
1130 case ZEND_TYPE_CHECK:
1131 SKIP_IF_TOP(op1);
1132 ct_eval_type_check(&zv, opline->extended_value, op1);
1133 SET_RESULT(result, &zv);
1134 zval_ptr_dtor_nogc(&zv);
1135 break;
1136 case ZEND_INSTANCEOF:
1137 SKIP_IF_TOP(op1);
1138 ZVAL_FALSE(&zv);
1139 SET_RESULT(result, &zv);
1140 break;
1141 case ZEND_ROPE_INIT:
1142 SKIP_IF_TOP(op2);
1143 if (zend_optimizer_eval_cast(&zv, IS_STRING, op2) == SUCCESS) {
1144 SET_RESULT(result, &zv);
1145 zval_ptr_dtor_nogc(&zv);
1146 break;
1147 }
1148 SET_RESULT_BOT(result);
1149 break;
1150 case ZEND_ROPE_ADD:
1151 case ZEND_ROPE_END:
1152 // TODO The way this is currently implemented will result in quadratic runtime
1153 // This is not necessary, the way the algorithm works it's okay to reuse the same
1154 // string for all SSA vars with some extra checks
1155 SKIP_IF_TOP(op1);
1156 SKIP_IF_TOP(op2);
1157 if (zend_optimizer_eval_binary_op(&zv, ZEND_CONCAT, op1, op2) == SUCCESS) {
1158 SET_RESULT(result, &zv);
1159 zval_ptr_dtor_nogc(&zv);
1160 break;
1161 }
1162 SET_RESULT_BOT(result);
1163 break;
1164 case ZEND_INIT_ARRAY:
1165 case ZEND_ADD_ARRAY_ELEMENT:
1166 {
1167 zval *result = NULL;
1168 if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
1169 SET_RESULT_BOT(result);
1170 SET_RESULT_BOT(op1);
1171 break;
1172 }
1173
1174 if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
1175 result = &ctx->values[ssa_op->result_use];
1176 if (IS_BOT(result)) {
1177 SET_RESULT_BOT(result);
1178 break;
1179 }
1180 SKIP_IF_TOP(result);
1181 }
1182
1183 SKIP_IF_TOP(op1);
1184 if (op2) {
1185 SKIP_IF_TOP(op2);
1186 }
1187
1188 /* We want to avoid keeping around intermediate arrays for each SSA variable in the
1189 * ADD_ARRAY_ELEMENT chain. We do this by only keeping the array on the last opcode
1190 * and use a NULL value everywhere else. */
1191 if (Z_TYPE(ctx->values[ssa_op->result_def]) == IS_NULL) {
1192 break;
1193 }
1194
1195 if (result) {
1196 ZVAL_COPY_VALUE(&zv, result);
1197 ZVAL_NULL(result);
1198 } else {
1199 array_init(&zv);
1200 }
1201
1202 if (ct_eval_add_array_elem(&zv, op1, op2) == SUCCESS) {
1203 SET_RESULT(result, &zv);
1204 zval_ptr_dtor_nogc(&zv);
1205 break;
1206 }
1207 SET_RESULT_BOT(result);
1208 zval_ptr_dtor_nogc(&zv);
1209 break;
1210 }
1211 case ZEND_ASSIGN_DIM:
1212 {
1213 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1214 if (IS_BOT(data)) {
1215 SET_RESULT_BOT(op1);
1216 SET_RESULT_BOT(result);
1217 break;
1218 }
1219
1220 SKIP_IF_TOP(data);
1221 SKIP_IF_TOP(op1);
1222 if (op2) {
1223 SKIP_IF_TOP(op2);
1224 }
1225
1226 ZVAL_DUP(&zv, op1);
1227 if (ct_eval_assign_dim(&zv, data, op2) == SUCCESS) {
1228 SET_RESULT(result, data);
1229 SET_RESULT(op1, &zv);
1230 zval_ptr_dtor_nogc(&zv);
1231 break;
1232 }
1233 SET_RESULT_BOT(result);
1234 SET_RESULT_BOT(op1);
1235 zval_ptr_dtor_nogc(&zv);
1236 break;
1237 }
1238 case ZEND_DO_ICALL:
1239 {
1240 zend_call_info *call;
1241 zval *name, *args[3] = {NULL};
1242 int i;
1243
1244 if (!ctx->call_map) {
1245 SET_RESULT_BOT(result);
1246 break;
1247 }
1248
1249 call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1250 name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
1251
1252 /* We already know it can't be evaluated, don't bother checking again */
1253 if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
1254 break;
1255 }
1256
1257 /* We're only interested in functions with up to three arguments right now */
1258 if (call->num_args > 3) {
1259 SET_RESULT_BOT(result);
1260 break;
1261 }
1262
1263 for (i = 0; i < call->num_args; i++) {
1264 zend_op *opline = call->arg_info[i].opline;
1265 if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) {
1266 SET_RESULT_BOT(result);
1267 return;
1268 }
1269
1270 args[i] = get_op1_value(ctx, opline,
1271 &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
1272 if (args[i]) {
1273 if (IS_BOT(args[i])) {
1274 SET_RESULT_BOT(result);
1275 return;
1276 } else if (IS_TOP(args[i])) {
1277 return;
1278 }
1279 }
1280 }
1281
1282 /* We didn't get a BOT argument, so value stays the same */
1283 if (!IS_TOP(&ctx->values[ssa_op->result_def])) {
1284 break;
1285 }
1286
1287 if (ct_eval_func_call(&zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
1288 SET_RESULT(result, &zv);
1289 zval_ptr_dtor_nogc(&zv);
1290 break;
1291 }
1292
1293 #if 0
1294 /* sort out | uniq -c | sort -n */
1295 fprintf(stderr, "%s\n", Z_STRVAL_P(name));
1296 /*if (args[1]) {
1297 php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]);
1298 } else {
1299 php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]);
1300 }*/
1301 #endif
1302
1303 SET_RESULT_BOT(result);
1304 break;
1305 }
1306 default:
1307 {
1308 /* If we have no explicit implementation return BOT */
1309 SET_RESULT_BOT(result);
1310 SET_RESULT_BOT(op1);
1311 SET_RESULT_BOT(op2);
1312 break;
1313 }
1314 }
1315 }
1316
1317 /* 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)1318 static void sccp_mark_feasible_successors(
1319 scdf_ctx *scdf,
1320 int block_num, zend_basic_block *block,
1321 zend_op *opline, zend_ssa_op *ssa_op) {
1322 sccp_ctx *ctx = (sccp_ctx *) scdf;
1323 zval *op1;
1324 int s;
1325
1326 /* We can't determine the branch target at compile-time for these */
1327 switch (opline->opcode) {
1328 case ZEND_ASSERT_CHECK:
1329 case ZEND_CATCH:
1330 case ZEND_DECLARE_ANON_CLASS:
1331 case ZEND_DECLARE_ANON_INHERITED_CLASS:
1332 case ZEND_FE_FETCH_R:
1333 case ZEND_FE_FETCH_RW:
1334 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1335 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1336 return;
1337 }
1338
1339 op1 = get_op1_value(ctx, opline, ssa_op);
1340
1341 /* Branch target can be either one */
1342 if (!op1 || IS_BOT(op1)) {
1343 for (s = 0; s < block->successors_count; s++) {
1344 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1345 }
1346 return;
1347 }
1348
1349 /* Branch target not yet known */
1350 if (IS_TOP(op1)) {
1351 return;
1352 }
1353
1354 switch (opline->opcode) {
1355 case ZEND_JMPZ:
1356 case ZEND_JMPZNZ:
1357 case ZEND_JMPZ_EX:
1358 s = zend_is_true(op1);
1359 break;
1360 case ZEND_JMPNZ:
1361 case ZEND_JMPNZ_EX:
1362 case ZEND_JMP_SET:
1363 s = !zend_is_true(op1);
1364 break;
1365 case ZEND_COALESCE:
1366 s = (Z_TYPE_P(op1) == IS_NULL);
1367 break;
1368 case ZEND_FE_RESET_R:
1369 case ZEND_FE_RESET_RW:
1370 if (Z_TYPE_P(op1) != IS_ARRAY) {
1371 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1372 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1373 return;
1374 }
1375 s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
1376 break;
1377 default:
1378 for (s = 0; s < block->successors_count; s++) {
1379 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1380 }
1381 return;
1382 }
1383 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1384 }
1385
join_phi_values(zval * a,zval * b)1386 static void join_phi_values(zval *a, zval *b) {
1387 if (IS_BOT(a) || IS_TOP(b)) {
1388 return;
1389 }
1390 if (IS_TOP(a)) {
1391 zval_ptr_dtor_nogc(a);
1392 ZVAL_COPY(a, b);
1393 return;
1394 }
1395 if (IS_BOT(b) || !zend_is_identical(a, b)) {
1396 zval_ptr_dtor_nogc(a);
1397 MAKE_BOT(a);
1398 }
1399 }
1400
sccp_visit_phi(scdf_ctx * scdf,zend_ssa_phi * phi)1401 static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
1402 sccp_ctx *ctx = (sccp_ctx *) scdf;
1403 zend_ssa *ssa = scdf->ssa;
1404 ZEND_ASSERT(phi->ssa_var >= 0);
1405 if (!IS_BOT(&ctx->values[phi->ssa_var])) {
1406 zend_basic_block *block = &ssa->cfg.blocks[phi->block];
1407 int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
1408
1409 int i;
1410 zval result;
1411 MAKE_TOP(&result);
1412 SCP_DEBUG("Handling PHI(");
1413 if (phi->pi >= 0) {
1414 ZEND_ASSERT(phi->sources[0] >= 0);
1415 if (scdf_is_edge_feasible(scdf, phi->pi, phi->block)) {
1416 join_phi_values(&result, &ctx->values[phi->sources[0]]);
1417 }
1418 } else {
1419 for (i = 0; i < block->predecessors_count; i++) {
1420 ZEND_ASSERT(phi->sources[i] >= 0);
1421 if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) {
1422 SCP_DEBUG("val, ");
1423 join_phi_values(&result, &ctx->values[phi->sources[i]]);
1424 } else {
1425 SCP_DEBUG("--, ");
1426 }
1427 }
1428 }
1429 SCP_DEBUG(")\n");
1430
1431 set_value(scdf, ctx, phi->ssa_var, &result);
1432 zval_ptr_dtor_nogc(&result);
1433 }
1434 }
1435
value_from_type_and_range(sccp_ctx * ctx,int var_num,zval * tmp)1436 static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
1437 zend_ssa *ssa = ctx->scdf.ssa;
1438 zend_ssa_var_info *info = &ssa->var_info[var_num];
1439
1440 if (ssa->vars[var_num].var >= ctx->scdf.op_array->last_var) {
1441 // TODO Non-CVs may cause issues with FREEs
1442 return NULL;
1443 }
1444
1445 if (info->type & MAY_BE_UNDEF) {
1446 return NULL;
1447 }
1448
1449 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_NULL))) {
1450 ZVAL_NULL(tmp);
1451 return tmp;
1452 }
1453 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_FALSE))) {
1454 ZVAL_FALSE(tmp);
1455 return tmp;
1456 }
1457 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_TRUE))) {
1458 ZVAL_TRUE(tmp);
1459 return tmp;
1460 }
1461
1462 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))
1463 && info->has_range
1464 && !info->range.overflow && !info->range.underflow
1465 && info->range.min == info->range.max) {
1466 ZVAL_LONG(tmp, info->range.min);
1467 return tmp;
1468 }
1469
1470 return NULL;
1471 }
1472
1473 /* This will try to replace uses of SSA variables we have determined to be constant. Not all uses
1474 * can be replaced, because some instructions don't accept constant operands or only accept them
1475 * if they have a certain type. */
replace_constant_operands(sccp_ctx * ctx)1476 static int replace_constant_operands(sccp_ctx *ctx) {
1477 zend_ssa *ssa = ctx->scdf.ssa;
1478 zend_op_array *op_array = ctx->scdf.op_array;
1479 int i;
1480 zval tmp;
1481 int removed_ops = 0;
1482
1483 /* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
1484 * and INIT_ARRAY. */
1485 for (i = ssa->vars_count - 1; i >= op_array->last_var; i--) {
1486 zend_ssa_var *var = &ssa->vars[i];
1487 zval *value;
1488 int use;
1489
1490 if (value_known(&ctx->values[i])) {
1491 value = &ctx->values[i];
1492 } else {
1493 value = value_from_type_and_range(ctx, i, &tmp);
1494 if (!value) {
1495 continue;
1496 }
1497 }
1498
1499 FOREACH_USE(var, use) {
1500 zend_op *opline = &op_array->opcodes[use];
1501 zend_ssa_op *ssa_op = &ssa->ops[use];
1502 if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
1503 if (opline->opcode == ZEND_NOP) {
1504 removed_ops++;
1505 }
1506 ZEND_ASSERT(ssa_op->op1_def == -1);
1507 if (ssa_op->op1_use != ssa_op->op2_use) {
1508 zend_ssa_unlink_use_chain(ssa, use, ssa_op->op1_use);
1509 } else {
1510 ssa_op->op2_use_chain = ssa_op->op1_use_chain;
1511 }
1512 ssa_op->op1_use = -1;
1513 ssa_op->op1_use_chain = -1;
1514 }
1515 if (try_replace_op2(ctx, opline, ssa_op, i, value)) {
1516 ZEND_ASSERT(ssa_op->op2_def == -1);
1517 if (ssa_op->op2_use != ssa_op->op1_use) {
1518 zend_ssa_unlink_use_chain(ssa, use, ssa_op->op2_use);
1519 }
1520 ssa_op->op2_use = -1;
1521 ssa_op->op2_use_chain = -1;
1522 }
1523 } FOREACH_USE_END();
1524
1525 /* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
1526 * value(s) were determined by SCCP. It removes dead computational instructions and converts
1527 * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
1528 * a) During operand replacement we eliminate FREEs. The corresponding computational instructions
1529 * must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
1530 * b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
1531 * and can't be DCEd. This means that it will not be able collect all instructions rendered dead
1532 * by SCCP, because they may have potentially side-effecting types, but the actual values are
1533 * not. As such doing DCE here will allow us to eliminate more dead code in combination.
1534 * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
1535 * we need to collect. */
1536
1537 if (var->definition >= 0 && value_known(&ctx->values[i])) {
1538 zend_op *opline = &op_array->opcodes[var->definition];
1539 zend_ssa_op *ssa_op = &ssa->ops[var->definition];
1540 if (opline->opcode == ZEND_ASSIGN) {
1541 /* Leave assigns to DCE (due to dtor effects) */
1542 continue;
1543 }
1544
1545 if (ssa_op->result_def == i
1546 && ssa_op->op1_def < 0
1547 && ssa_op->op2_def < 0
1548 && var->use_chain < 0
1549 && var->phi_use_chain == NULL) {
1550 if (opline->opcode == ZEND_DO_ICALL) {
1551 /* Call instruction -> remove opcodes that are part of the call */
1552 zend_call_info *call;
1553 int i;
1554
1555 ZEND_ASSERT(ctx->call_map);
1556 call = ctx->call_map[var->definition];
1557 ZEND_ASSERT(call);
1558 ZEND_ASSERT(call->caller_call_opline == opline);
1559 if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
1560 zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition);
1561 }
1562 zend_ssa_remove_result_def(ssa, ssa_op);
1563 zend_ssa_remove_instr(ssa, opline, ssa_op);
1564 zend_ssa_remove_instr(ssa, call->caller_init_opline,
1565 &ssa->ops[call->caller_init_opline - op_array->opcodes]);
1566
1567 for (i = 0; i < call->num_args; i++) {
1568 zend_ssa_remove_instr(ssa, call->arg_info[i].opline,
1569 &ssa->ops[call->arg_info[i].opline - op_array->opcodes]);
1570 }
1571 removed_ops = call->num_args + 2;
1572
1573 // TODO: remove call_info completely???
1574 call->callee_func = NULL;
1575 } else {
1576 /* Ordinary computational instruction -> remove it */
1577 if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
1578 zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition);
1579 }
1580 zend_ssa_remove_result_def(ssa, ssa_op);
1581 zend_ssa_remove_instr(ssa, opline, ssa_op);
1582 removed_ops++;
1583 }
1584 } else if (ssa_op->op1_def == i &&
1585 (ssa_op->result_def < 0 ||
1586 (ssa->vars[ssa_op->result_def].use_chain < 0 &&
1587 ssa->vars[ssa_op->result_def].phi_use_chain == NULL))) {
1588 /* Compound assign or incdec -> convert to direct ASSIGN */
1589
1590 /* Destroy previous op2 */
1591 if (opline->op2_type == IS_CONST) {
1592 literal_dtor(&ZEND_OP2_LITERAL(opline));
1593 } else if (ssa_op->op2_use >= 0) {
1594 if (ssa_op->op2_use != ssa_op->op1_use) {
1595 zend_ssa_unlink_use_chain(ssa, var->definition, ssa_op->op2_use);
1596 }
1597 ssa_op->op2_use = -1;
1598 ssa_op->op2_use_chain = -1;
1599 }
1600
1601 /* We checked that result has no uses, mark unused */
1602 if (ssa_op->result_def >= 0) {
1603 if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
1604 zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition);
1605 }
1606 zend_ssa_remove_result_def(ssa, ssa_op);
1607 opline->result_type = IS_UNUSED;
1608 }
1609
1610 /* Remove OP_DATA opcode */
1611 if (opline->opcode == ZEND_ASSIGN_DIM) {
1612 removed_ops++;
1613 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
1614 }
1615
1616 /* Convert to ASSIGN */
1617 opline->opcode = ZEND_ASSIGN;
1618 opline->op2_type = IS_CONST;
1619 opline->op2.constant = zend_optimizer_add_literal(op_array, value);
1620 Z_TRY_ADDREF_P(value);
1621 }
1622 }
1623 if (var->definition_phi
1624 && value_known(&ctx->values[i])
1625 && var->use_chain < 0
1626 && var->phi_use_chain == NULL) {
1627 zend_ssa_remove_phi(ssa, var->definition_phi);
1628 }
1629 }
1630
1631 return removed_ops;
1632 }
1633
sccp_context_init(zend_optimizer_ctx * ctx,sccp_ctx * sccp,zend_ssa * ssa,zend_op_array * op_array,zend_call_info ** call_map)1634 static void sccp_context_init(zend_optimizer_ctx *ctx, sccp_ctx *sccp,
1635 zend_ssa *ssa, zend_op_array *op_array, zend_call_info **call_map) {
1636 int i;
1637 sccp->call_map = call_map;
1638 sccp->values = zend_arena_alloc(&ctx->arena, sizeof(zval) * ssa->vars_count);
1639
1640 MAKE_TOP(&sccp->top);
1641 MAKE_BOT(&sccp->bot);
1642
1643 i = 0;
1644 for (; i < op_array->last_var; ++i) {
1645 /* These are all undefined variables, which we have to mark BOT.
1646 * Otherwise the undefined variable warning might not be preserved. */
1647 MAKE_BOT(&sccp->values[i]);
1648 }
1649 for (; i < ssa->vars_count; ++i) {
1650 if (ssa->vars[i].alias) {
1651 MAKE_BOT(&sccp->values[i]);
1652 } else {
1653 MAKE_TOP(&sccp->values[i]);
1654 }
1655 }
1656 }
1657
sccp_context_free(sccp_ctx * sccp)1658 static void sccp_context_free(sccp_ctx *sccp) {
1659 int i;
1660 for (i = sccp->scdf.op_array->last_var; i < sccp->scdf.ssa->vars_count; ++i) {
1661 zval_ptr_dtor_nogc(&sccp->values[i]);
1662 }
1663 }
1664
sccp_optimize_op_array(zend_optimizer_ctx * ctx,zend_op_array * op_array,zend_ssa * ssa,zend_call_info ** call_map)1665 int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
1666 {
1667 sccp_ctx sccp;
1668 int removed_ops = 0;
1669 void *checkpoint = zend_arena_checkpoint(ctx->arena);
1670
1671 sccp_context_init(ctx, &sccp, ssa, op_array, call_map);
1672
1673 sccp.scdf.handlers.visit_instr = sccp_visit_instr;
1674 sccp.scdf.handlers.visit_phi = sccp_visit_phi;
1675 sccp.scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors;
1676
1677 scdf_init(ctx, &sccp.scdf, op_array, ssa);
1678 scdf_solve(&sccp.scdf, "SCCP");
1679
1680 removed_ops += scdf_remove_unreachable_blocks(&sccp.scdf);
1681 removed_ops += replace_constant_operands(&sccp);
1682
1683 sccp_context_free(&sccp);
1684 zend_arena_release(&ctx->arena, checkpoint);
1685
1686 return removed_ops;
1687 }
1688