]> granicus.if.org Git - php/commitdiff
Fixed bug #52614 (Memory leak when writing on uninitialized variable returned from...
authorDmitry Stogov <dmitry@php.net>
Wed, 25 Aug 2010 09:14:36 +0000 (09:14 +0000)
committerDmitry Stogov <dmitry@php.net>
Wed, 25 Aug 2010 09:14:36 +0000 (09:14 +0000)
NEWS
Zend/tests/bug52614.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_language_parser.y
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.h

diff --git a/NEWS b/NEWS
index bc4b6f4d0c54f8848bfdc6e39f99ef74761044ad..5bae8b202e903960034b1c02ededf4f8f25e0c8e 100644 (file)
--- a/NEWS
+++ b/NEWS
 - Fixed a NULL pointer dereference when processing invalid XML-RPC
   requests (Fixes CVE-2010-0397, bug #51288). (Raphael Geissert)
 
+- Fixed bug #52614 (Memory leak when writing on uninitialized variable returned
+  from method call). (Dmitry)
 - Fixed bug #51338 (URL-Rewriter is still enabled if use_only_cookies is
   on). (Ilia, j dot jeising at gmail dot com)
 - Fixed bug #51269 (zlib.output_compression Overwrites Vary Header). (Adam)
diff --git a/Zend/tests/bug52614.phpt b/Zend/tests/bug52614.phpt
new file mode 100644 (file)
index 0000000..38a210b
--- /dev/null
@@ -0,0 +1,83 @@
+--TEST--
+Bug #52614 (Memory leak when writing on uninitialized variable returned from method call)
+--FILE--
+<?php
+class foo {
+       public $a1;
+       public $a2 = array();
+       public $a3;
+       public $o1;
+       public $o2;
+       
+       public function f1() {
+               return $this->a1;
+       }
+
+       public function f2() {
+               return $this->a2;
+       }
+
+       public function f3() {
+               $this->a3 = array();
+               return $this->a3;
+       }
+
+       public function f4() {
+               return $this->o1;
+       }
+
+       public function f5() {
+               $this->o2 = new stdClass;
+               return $this->o2;
+       }
+
+       public function &f6() {
+               return $this->a1;
+       }
+
+       public function f7(&$x) {
+               $x = 2;
+       }
+
+}
+
+$foo = new foo;
+
+$foo->f1()[0] = 1;
+var_dump($foo->a1);
+
+$foo->f2()[0] = 1;
+var_dump($foo->a2);
+
+$foo->f3()[0] = 1;
+var_dump($foo->a3);
+
+$foo->f4()->a = 1;
+var_dump($foo->o1);
+
+$foo->f5()->a = 1;
+var_dump($foo->o2);
+
+$foo->a1[0] = 1;
+$foo->f7($foo->f6()[0]);
+var_dump($foo->a1[0]);
+$foo->f1()[0]++;
+var_dump($foo->a1[0]);
+$foo->f6()[0]++;
+var_dump($foo->a1[0]);
+--EXPECTF--
+NULL
+array(0) {
+}
+array(0) {
+}
+
+Strict Standards: Creating default object from empty value in %sbug52614.php on line 52
+NULL
+object(stdClass)#%d (1) {
+  ["a"]=>
+  int(1)
+}
+int(2)
+int(2)
+int(3)
index 66fdec13f1c72493a72d2a53902a81a35d978935..4720dec1e462e3e0adb789272e58832afd3e41f8 100644 (file)
@@ -550,6 +550,14 @@ int zend_add_const_name_literal(zend_op_array *op_array, const zval *zv, int unq
                op.constant = zend_add_literal(CG(active_op_array), &_c); \
        } while (0)
 
+static inline zend_bool zend_is_function_or_method_call(const znode *variable) /* {{{ */
+{
+       zend_uint type = variable->EA;
+
+       return  ((type & ZEND_PARSED_METHOD_CALL) || (type == ZEND_PARSED_FUNCTION_CALL));
+}
+/* }}} */
+
 void zend_do_binary_op(zend_uchar op, znode *result, const znode *op1, const znode *op2 TSRMLS_DC) /* {{{ */
 {
        zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
@@ -781,6 +789,18 @@ void fetch_array_dim(znode *result, const znode *parent, const znode *dim TSRMLS
        zend_op opline;
        zend_llist *fetch_list_ptr;
 
+       zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr);
+
+       if (zend_is_function_or_method_call(parent)) {
+               init_op(&opline TSRMLS_CC);
+               opline.opcode = ZEND_SEPARATE;
+               SET_NODE(opline.op1, parent);
+               SET_UNUSED(opline.op2);
+               opline.result_type = IS_VAR;
+               opline.result.var = opline.op1.var;
+               zend_llist_add_element(fetch_list_ptr, &opline);
+       }
+       
        init_op(&opline TSRMLS_CC);
        opline.opcode = ZEND_FETCH_DIM_W;       /* the backpatching routine assumes W */
        opline.result_type = IS_VAR;
@@ -802,7 +822,6 @@ void fetch_array_dim(znode *result, const znode *parent, const znode *dim TSRMLS
        
        GET_NODE(result, opline.result);
 
-       zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr);
        zend_llist_add_element(fetch_list_ptr, &opline);
 }
 /* }}} */
@@ -985,14 +1004,6 @@ void zend_do_assign(znode *result, znode *variable, znode *value TSRMLS_DC) /* {
 }
 /* }}} */
 
-static inline zend_bool zend_is_function_or_method_call(const znode *variable) /* {{{ */
-{
-       zend_uint type = variable->EA;
-
-       return  ((type & ZEND_PARSED_METHOD_CALL) || (type == ZEND_PARSED_FUNCTION_CALL));
-}
-/* }}} */
-
 void zend_do_assign_ref(znode *result, const znode *lvar, const znode *rvar TSRMLS_DC) /* {{{ */
 {
        zend_op *opline;
@@ -1307,6 +1318,14 @@ void zend_do_end_variable_parse(znode *variable, int type, int arg_offset TSRMLS
 
                while (le) {
                        opline_ptr = (zend_op *)le->data;
+                       if (opline_ptr->opcode == ZEND_SEPARATE) {
+                               if (type != BP_VAR_R && type != BP_VAR_IS) {
+                                       opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+                                       memcpy(opline, opline_ptr, sizeof(zend_op));
+                               }
+                               le = le->next;
+                               continue;
+                       }
                        opline = get_next_op(CG(active_op_array) TSRMLS_CC);
                        memcpy(opline, opline_ptr, sizeof(zend_op));
                        if (opline->op1_type == IS_VAR &&
@@ -4865,6 +4884,16 @@ void zend_do_fetch_property(znode *result, znode *object, const znode *property
                }
        }
 
+       if (zend_is_function_or_method_call(object)) {
+               init_op(&opline TSRMLS_CC);
+               opline.opcode = ZEND_SEPARATE;
+               SET_NODE(opline.op1, object);
+               SET_UNUSED(opline.op2);
+               opline.result_type = IS_VAR;
+               opline.result.var = opline.op1.var;
+               zend_llist_add_element(fetch_list_ptr, &opline);
+       }
+
        init_op(&opline TSRMLS_CC);
        opline.opcode = ZEND_FETCH_OBJ_W;       /* the backpatching routine assumes W */
        opline.result_type = IS_VAR;
@@ -5745,7 +5774,11 @@ void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token
                        if (fetch->opcode == ZEND_FETCH_DIM_W && fetch->op2_type == IS_UNUSED) {
                                zend_error(E_COMPILE_ERROR, "Cannot use [] for reading");
                        }
-                       fetch->opcode -= 3; /* FETCH_W -> FETCH_R */
+                       if (fetch->opcode == ZEND_SEPARATE) {
+                               MAKE_NOP(fetch);
+                       } else {
+                               fetch->opcode -= 3; /* FETCH_W -> FETCH_R */
+                       }
                }
                /* prevent double SWITCH_FREE */
                zend_stack_top(&CG(foreach_copy_stack), (void **) &foreach_copy);
index 5e448462a0bf498d5cf57416e59fe34ec6dbb449..6d2432eaea4df601cadb6dc3f5ed37afdca0fb6d 100644 (file)
@@ -932,7 +932,7 @@ variable_property:
 
 array_method_dereference:
                array_method_dereference '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
-       |       method '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
+       |       method '[' dim_offset ']' { $1.EA = ZEND_PARSED_METHOD_CALL; fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
 ;
 
 method:
@@ -942,7 +942,7 @@ method:
 ;
 
 method_or_not:                 
-               method                                          { $$ = $1; zend_do_push_object(&$$ TSRMLS_CC); $$.EA = ZEND_PARSED_METHOD_CALL; }
+               method                                          { $$ = $1; $$.EA = ZEND_PARSED_METHOD_CALL; zend_do_push_object(&$$ TSRMLS_CC); }
        |       array_method_dereference        { $$ = $1; zend_do_push_object(&$$ TSRMLS_CC); }
        |       /* empty */ { $$.EA = ZEND_PARSED_MEMBER; }
 ;
@@ -964,7 +964,7 @@ variable_class_name:
 
 array_function_dereference:
                array_function_dereference '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
-       |       function_call { zend_do_begin_variable_parse(TSRMLS_C); $$.EA = ZEND_PARSED_FUNCTION_CALL; } 
+       |       function_call { zend_do_begin_variable_parse(TSRMLS_C); $1.EA = ZEND_PARSED_FUNCTION_CALL; } 
                '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$4 TSRMLS_CC); }
 ;
 
index bae4c995ff350dabea9bdd221d5ac47798c49a76..9b274cc615d6b149ee1516c81e4061a78aff4a4b 100644 (file)
@@ -4998,4 +4998,25 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)
        ZEND_VM_NEXT_OPCODE();
 }
 
+ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED)
+{
+       USE_OPLINE
+       zval *var_ptr, *new_zv;
+
+       SAVE_OPLINE();
+       var_ptr = EX_T(opline->op1.var).var.ptr;
+       if (Z_TYPE_P(var_ptr) != IS_OBJECT &&
+           !PZVAL_IS_REF(var_ptr) &&
+           Z_REFCOUNT_P(var_ptr) > 1) {
+
+               Z_DELREF_P(var_ptr);
+               ALLOC_ZVAL(new_zv);
+               INIT_PZVAL_COPY(new_zv, var_ptr);
+               var_ptr = new_zv;
+               zval_copy_ctor(var_ptr);
+               EX_T(opline->op1.var).var.ptr = var_ptr;
+       }
+       ZEND_VM_NEXT_OPCODE();
+}
+
 ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper)
index 3b07493c526f38455f4827b280f7705dc86b2572..4da5485d37cb66e189ea480a13c22d04be81f197 100644 (file)
@@ -18807,6 +18807,27 @@ static int ZEND_FASTCALL  ZEND_ISSET_ISEMPTY_VAR_SPEC_VAR_UNUSED_HANDLER(ZEND_OP
        ZEND_VM_NEXT_OPCODE();
 }
 
+static int ZEND_FASTCALL  ZEND_SEPARATE_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+       zval *var_ptr, *new_zv;
+
+       SAVE_OPLINE();
+       var_ptr = EX_T(opline->op1.var).var.ptr;
+       if (Z_TYPE_P(var_ptr) != IS_OBJECT &&
+           !PZVAL_IS_REF(var_ptr) &&
+           Z_REFCOUNT_P(var_ptr) > 1) {
+
+               Z_DELREF_P(var_ptr);
+               ALLOC_ZVAL(new_zv);
+               INIT_PZVAL_COPY(new_zv, var_ptr);
+               var_ptr = new_zv;
+               zval_copy_ctor(var_ptr);
+               EX_T(opline->op1.var).var.ptr = var_ptr;
+       }
+       ZEND_VM_NEXT_OPCODE();
+}
+
 static int ZEND_FASTCALL  ZEND_ADD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
@@ -39488,6 +39509,31 @@ void zend_init_opcodes_handlers(void)
        ZEND_BIND_TRAITS_SPEC_HANDLER,
        ZEND_BIND_TRAITS_SPEC_HANDLER,
        ZEND_BIND_TRAITS_SPEC_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_SEPARATE_SPEC_VAR_UNUSED_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER
   };
   zend_opcode_handlers = (opcode_handler_t*)labels;
index 7cd8add0f844a25b166ca3100b373ec25d4371fe..4cd1ac43466a37df2785057cba1da8433fd0ee0b 100644 (file)
 #define ZEND_DECLARE_LAMBDA_FUNCTION         153
 #define ZEND_ADD_TRAIT                       154
 #define ZEND_BIND_TRAITS                     155
+#define ZEND_SEPARATE                        156