summaryrefslogtreecommitdiff
path: root/Zend
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@php.net>2010-08-25 09:14:36 +0000
committerDmitry Stogov <dmitry@php.net>2010-08-25 09:14:36 +0000
commit2188f26c45375358ac872a61b6de76fc85b83be4 (patch)
treef58577a62b779a539271f4f8641e7a957d115c39 /Zend
parent45960c90302bb60a22e11a779d866b9eed909c33 (diff)
downloadphp-git-2188f26c45375358ac872a61b6de76fc85b83be4.tar.gz
Fixed bug #52614 (Memory leak when writing on uninitialized variable returned from method call)
Diffstat (limited to 'Zend')
-rw-r--r--Zend/tests/bug52614.phpt83
-rw-r--r--Zend/zend_compile.c53
-rw-r--r--Zend/zend_language_parser.y6
-rw-r--r--Zend/zend_vm_def.h21
-rw-r--r--Zend/zend_vm_execute.h46
-rw-r--r--Zend/zend_vm_opcodes.h1
6 files changed, 197 insertions, 13 deletions
diff --git a/Zend/tests/bug52614.phpt b/Zend/tests/bug52614.phpt
new file mode 100644
index 0000000000..38a210b9a9
--- /dev/null
+++ b/Zend/tests/bug52614.phpt
@@ -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)
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 66fdec13f1..4720dec1e4 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -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);
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 5e448462a0..6d2432eaea 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -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); }
;
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index bae4c995ff..9b274cc615 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -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)
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 3b07493c52..4da5485d37 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -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;
diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h
index 7cd8add0f8..4cd1ac4346 100644
--- a/Zend/zend_vm_opcodes.h
+++ b/Zend/zend_vm_opcodes.h
@@ -156,3 +156,4 @@
#define ZEND_DECLARE_LAMBDA_FUNCTION 153
#define ZEND_ADD_TRAIT 154
#define ZEND_BIND_TRAITS 155
+#define ZEND_SEPARATE 156