diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | Zend/tests/bug72944.phpt | 12 | ||||
-rw-r--r-- | Zend/zend_compile.c | 34 | ||||
-rw-r--r-- | Zend/zend_compile.h | 1 | ||||
-rw-r--r-- | ext/opcache/Optimizer/block_pass.c | 17 | ||||
-rw-r--r-- | ext/opcache/Optimizer/dfa_pass.c | 15 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_cfg.c | 9 |
7 files changed, 74 insertions, 15 deletions
@@ -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--; } |