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.c33
5 files changed, 77 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 12cc50d064..522076e9c7 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ PHP NEWS
?? ??? 2016 PHP 7.0.11
- 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 #72911 (Memleak in zend_binary_assign_op_obj_helper). (Laruence)
. Fixed bug #72813 (Segfault with __get returned by ref). (Laruence)
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 73ccd4a174..7c4d9d5e1d 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -1944,10 +1944,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 bbd2df7f2e..f946448ffa 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -761,6 +761,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(zend_uchar 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 5e9b20d191..4b532c4b20 100644
--- a/ext/opcache/Optimizer/block_pass.c
+++ b/ext/opcache/Optimizer/block_pass.c
@@ -561,7 +561,7 @@ static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int
convert_to_string((v)); \
}
-static void strip_nop(zend_code_block *block, zend_optimizer_ctx *ctx)
+static void strip_nop(zend_code_block *block, zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_op *opline = block->start_opline;
zend_op *end, *new_end;
@@ -575,6 +575,14 @@ static void strip_nop(zend_code_block *block, zend_optimizer_ctx *ctx)
}
return;
}
+ if (block->len == 2
+ && ((block->start_opline + 1)->opcode == ZEND_JMPZ
+ || (block->start_opline + 1)->opcode == ZEND_JMPNZ)
+ && (block->start_opline + 1)->op1_type & (IS_CV|IS_CONST)
+ && block->start_opline > op_array->opcodes
+ && zend_is_smart_branch(block->start_opline - 1)) {
+ break;
+ }
block->start_opline++;
block->start_opline_no++;
block->len--;
@@ -588,10 +596,21 @@ static void strip_nop(zend_code_block *block, zend_optimizer_ctx *ctx)
zend_op *src;
int len = 0;
+ src = opline;
while (opline < end && opline->opcode == ZEND_NOP) {
+ if (opline + 1 < end
+ && ((opline + 1)->opcode == ZEND_JMPZ
+ || (opline + 1)->opcode == ZEND_JMPNZ)
+ && (opline + 1)->op1_type & (IS_CV|IS_CONST)
+ && opline > op_array->opcodes
+ && zend_is_smart_branch(opline - 1)) {
+ /* don't remove NOP, that splits incorrect smart branch */
+ opline++;
+ break;
+ }
+ src++;
opline++;
}
- src = opline;
while (opline < end && opline->opcode != ZEND_NOP) {
opline++;
@@ -621,6 +640,14 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array,
if (block->follow_to) {
delete_code_block(block, ctx);
}
+ if (block->len == 2
+ && ((block->start_opline + 1)->opcode == ZEND_JMPZ
+ || (block->start_opline + 1)->opcode == ZEND_JMPNZ)
+ && (block->start_opline + 1)->op1_type & (IS_CV|IS_CONST)
+ && block->start_opline > op_array->opcodes
+ && zend_is_smart_branch(block->start_opline - 1)) {
+ break;
+ }
return;
}
block->start_opline++;
@@ -1137,7 +1164,7 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array,
opline++;
}
- strip_nop(block, ctx);
+ strip_nop(block, op_array, ctx);
}
/* Rebuild plain (optimized) op_array from CFG */