]> granicus.if.org Git - php/commitdiff
Fixed bug #72944 (Null pointer deref in zval_delref_p).
authorDmitry Stogov <dmitry@zend.com>
Mon, 29 Aug 2016 09:02:50 +0000 (12:02 +0300)
committerDmitry Stogov <dmitry@zend.com>
Mon, 29 Aug 2016 09:02:50 +0000 (12:02 +0300)
NEWS
Zend/tests/bug72944.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
ext/opcache/Optimizer/block_pass.c

diff --git a/NEWS b/NEWS
index 12cc50d064842a07838f90fc841651d6a78b39d0..522076e9c744f3960084c4e665fa64ae61091a1a 100644 (file)
--- 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 (file)
index 0000000..541730a
--- /dev/null
@@ -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
index 73ccd4a174c12be16e4b4efb3442c7ec27b883f5..7c4d9d5e1dc28ed89502808ad0a6bf4fd411c2bc 100644 (file)
@@ -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;
 }
index bbd2df7f2e8839bd447100bb919e720d34e44cfd..f946448ffabe3f3cd02595ebd53a672aac36b343 100644 (file)
@@ -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 {
index 5e9b20d191e49f13c2cc4ed1abb5e6069b01d77f..4b532c4b206c19c1571f16e7b2c91bea13f663fd 100644 (file)
@@ -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 */