summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--Zend/tests/bug72944.phpt12
-rw-r--r--Zend/zend_compile.c34
-rw-r--r--Zend/zend_compile.h1
-rw-r--r--ext/opcache/Optimizer/block_pass.c17
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c15
-rw-r--r--ext/opcache/Optimizer/zend_cfg.c9
7 files changed, 74 insertions, 15 deletions
diff --git a/NEWS b/NEWS
index 30415fe2c9..6dc4f1aeef 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ PHP NEWS
?? ??? 2016, PHP 7.1.0RC1
- Core:
+ . Fixed bug #72944 (Null pointer deref in zval_delref_p). (Dmitry)
. Fixed bug #72943 (assign_dim on string doesn't reset hval). (Laruence)
. Fixed bug #72598 (Reference is lost after array_slice()) (Nikita)
diff --git a/Zend/tests/bug72944.phpt b/Zend/tests/bug72944.phpt
new file mode 100644
index 0000000000..541730a22a
--- /dev/null
+++ b/Zend/tests/bug72944.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Bug #72944 (Null pointer deref in zval_delref_p).
+--FILE--
+<?php
+"a"== e & $A = $A? 0 : 0 ?:0;
+echo "OK\n";
+?>
+--EXPECTF--
+Notice: Use of undefined constant e - assumed 'e' in %sbug72944.php on line 2
+
+Notice: Undefined variable: A in %sbug72944.php on line 2
+OK
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index b61a819528..24da5209d7 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2198,10 +2198,42 @@ static inline uint32_t zend_emit_jump(uint32_t opnum_target) /* {{{ */
}
/* }}} */
+ZEND_API int zend_is_smart_branch(zend_op *opline) /* {{{ */
+{
+ switch (opline->opcode) {
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_CASE:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_INSTANCEOF:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ return 1;
+ default:
+ return 0;
+ }
+}
+/* }}} */
+
static inline uint32_t zend_emit_cond_jump(zend_uchar opcode, znode *cond, uint32_t opnum_target) /* {{{ */
{
uint32_t opnum = get_next_op_number(CG(active_op_array));
- zend_op *opline = zend_emit_op(NULL, opcode, cond, NULL);
+ zend_op *opline;
+
+ if ((cond->op_type & (IS_CV|IS_CONST))
+ && opnum > 0
+ && zend_is_smart_branch(CG(active_op_array)->opcodes + opnum - 1)) {
+ /* emit extra NOP to avoid incorrect SMART_BRANCH in very rare cases */
+ zend_emit_op(NULL, ZEND_NOP, NULL, NULL);
+ opnum = get_next_op_number(CG(active_op_array));
+ }
+ opline = zend_emit_op(NULL, opcode, cond, NULL);
opline->op2.opline_num = opnum_target;
return opnum;
}
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 56b80eed6a..1f0773d9a7 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -791,6 +791,7 @@ ZEND_API char *zend_make_compiled_string_description(const char *name);
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers);
uint32_t zend_get_class_fetch_type(zend_string *name);
ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc);
+ZEND_API int zend_is_smart_branch(zend_op *opline);
typedef zend_bool (*zend_auto_global_callback)(zend_string *name);
typedef struct _zend_auto_global {
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c
index 3bd86e60c0..3490a73e03 100644
--- a/ext/opcache/Optimizer/block_pass.c
+++ b/ext/opcache/Optimizer/block_pass.c
@@ -91,6 +91,15 @@ static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
zend_op *opcodes = op_array->opcodes;
while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP) {
+ /* check if NOP breaks incorrect smart branch */
+ if (b->len == 2
+ && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
+ || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
+ && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
+ && b->start > 0
+ && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
+ break;
+ }
b->start++;
b->len--;
}
@@ -114,6 +123,14 @@ static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
}
j++;
}
+ if (i + 1 < b->start + b->len
+ && (op_array->opcodes[i+1].opcode == ZEND_JMPZ
+ || op_array->opcodes[i+1].opcode == ZEND_JMPNZ)
+ && op_array->opcodes[i+1].op1_type & (IS_CV|IS_CONST)
+ && zend_is_smart_branch(op_array->opcodes + j - 1)) {
+ /* don't remove NOP, that splits incorrect smart branch */
+ j++;
+ }
i++;
}
b->len = j - b->start;
diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c
index a99d50fc41..63faaf62c3 100644
--- a/ext/opcache/Optimizer/dfa_pass.c
+++ b/ext/opcache/Optimizer/dfa_pass.c
@@ -149,20 +149,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa)
i + 1 < op_array->last &&
(op_array->opcodes[i+1].opcode == ZEND_JMPZ ||
op_array->opcodes[i+1].opcode == ZEND_JMPNZ) &&
- (op_array->opcodes[i-1].opcode == ZEND_IS_IDENTICAL ||
- op_array->opcodes[i-1].opcode == ZEND_IS_NOT_IDENTICAL ||
- op_array->opcodes[i-1].opcode == ZEND_IS_EQUAL ||
- op_array->opcodes[i-1].opcode == ZEND_IS_NOT_EQUAL ||
- op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER ||
- op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER_OR_EQUAL ||
- op_array->opcodes[i-1].opcode == ZEND_CASE ||
- op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_VAR ||
- op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP ||
- op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ ||
- op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ ||
- op_array->opcodes[i-1].opcode == ZEND_INSTANCEOF ||
- op_array->opcodes[i-1].opcode == ZEND_TYPE_CHECK ||
- op_array->opcodes[i-1].opcode == ZEND_DEFINED))) {
+ zend_is_smart_branch(op_array->opcodes + i - 1))) {
if (i != target) {
op_array->opcodes[target] = op_array->opcodes[i];
ssa->ops[target] = ssa->ops[i];
diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c
index 92d648dc23..04be6a2e26 100644
--- a/ext/opcache/Optimizer/zend_cfg.c
+++ b/ext/opcache/Optimizer/zend_cfg.c
@@ -109,6 +109,15 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *
b = blocks + block_map[live_range->start];
if (b->flags & ZEND_BB_REACHABLE) {
while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
+ /* check if NOP breaks incorrect smart branch */
+ if (b->len == 2
+ && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
+ || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
+ && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
+ && b->start > 0
+ && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
+ break;
+ }
b->start++;
b->len--;
}