summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-11-17 10:18:37 +0100
committerNikita Popov <nikita.ppv@gmail.com>2020-11-17 10:19:57 +0100
commitdf7417d1275c53d9b38d5c3ad5d9beb263a21e08 (patch)
tree402cc77bd9597b54db07906a0703e847bc005843
parent78773890f6b0d82b29e0b869f3f3b22174cea217 (diff)
downloadphp-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.phpt19
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c18
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