diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-11-17 10:18:37 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-11-17 10:19:57 +0100 |
commit | df7417d1275c53d9b38d5c3ad5d9beb263a21e08 (patch) | |
tree | 402cc77bd9597b54db07906a0703e847bc005843 | |
parent | 78773890f6b0d82b29e0b869f3f3b22174cea217 (diff) | |
download | php-git-df7417d1275c53d9b38d5c3ad5d9beb263a21e08.tar.gz |
Fix incorrectly optimized out live range
For x ? y : z style structures, the live range starts at z, but
may also hold the value of y. Make sure that the refcounting check
takes this into account, by checking the type of a potential phi
user.
-rw-r--r-- | Zend/tests/live_range_phi_leak.phpt | 19 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.c | 18 |
2 files changed, 33 insertions, 4 deletions
diff --git a/Zend/tests/live_range_phi_leak.phpt b/Zend/tests/live_range_phi_leak.phpt new file mode 100644 index 0000000000..f072584053 --- /dev/null +++ b/Zend/tests/live_range_phi_leak.phpt @@ -0,0 +1,19 @@ +--TEST-- +Missing live range if part of phi +--FILE-- +<?php +function doThrow() { + throw new Exception("Test"); +} +function test($k) { + // The 0 gives the QM_ASSIGN a non-refcounted type. + $res[$k ? $k : 0] = doThrow(); +} +try { + test(new stdClass); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Test diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 0349d0ffbe..7871fa3c8e 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -1312,11 +1312,21 @@ static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array) static zend_bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) { zend_func_info *func_info = ZEND_FUNC_INFO(op_array); zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes]; - if (ssa_op->result_def >= 0) { - uint32_t type = func_info->ssa.var_info[ssa_op->result_def].type; - return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0; + int ssa_var = ssa_op->result_def; + if (ssa_var < 0) { + /* Be conservative. */ + return 1; } - return 1; + + /* If the variable is used by a PHI, this may be the assignment of the final branch of a + * ternary/etc structure. While this is where the live range starts, the value from the other + * branch may also be used. As such, use the type of the PHI node for the following check. */ + if (func_info->ssa.vars[ssa_var].phi_use_chain) { + ssa_var = func_info->ssa.vars[ssa_var].phi_use_chain->ssa_var; + } + + uint32_t type = func_info->ssa.var_info[ssa_var].type; + return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0; } #endif |