From: Xinchen Hui <laruence@gmail.com>
Date: Tue, 28 Jun 2016 07:36:50 +0000 (+0800)
Subject: Fixed bug #72508 (strange references after recursive function call and "switch" state... 
X-Git-Tag: php-7.0.9RC1~13
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ce7e180a04009e821c87e7ae2b704294a2a97c03;p=php

Fixed bug #72508 (strange references after recursive function call and "switch" statement)
---

diff --git a/NEWS b/NEWS
index 816a20f451..33e4f5f015 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@ PHP                                                                        NEWS
 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 ?? ??? 2016 PHP 7.0.9
 
+- Core:
+  . Fixed bug #72508 (strange references after recursive function call and
+    "switch" statement). (Laruence)
+
 - CLI:
   . Fixed bug #72484 (SCRIPT_FILENAME shows wrong path if the user specify
     router.php). (Laruence)
diff --git a/Zend/tests/bug72508.phpt b/Zend/tests/bug72508.phpt
new file mode 100644
index 0000000000..60196bfc18
--- /dev/null
+++ b/Zend/tests/bug72508.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug #72508 (strange references after recursive function call and "switch" statement)
+--FILE--
+<?php
+function a ($option) {
+	b($option['bla']);
+	c($option);
+	var_dump($option);
+}
+function b (&$string) {
+	$string = 'changed';
+}
+function c ($option) {
+	switch ($option['bla']) {
+	default:
+		$copy = $option;
+		$copy['bla'] = 'copy';
+		break;
+	case NULL:
+		break;
+	}
+}
+a(array('bla' => 'fasel'));
+
+?>
+--EXPECT--
+array(1) {
+  ["bla"]=>
+  string(7) "changed"
+}
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 593c19e015..3af1fb3861 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -4035,16 +4035,32 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
 
 	znode expr_node, case_node;
 	zend_op *opline;
-	uint32_t *jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0);
-	uint32_t opnum_default_jmp;
+	uint32_t *jmpnz_opnums, opnum_default_jmp;
 
 	zend_compile_expr(&expr_node, expr_ast);
 
+	if (cases->children == 1 && cases->child[0]->child[0] == NULL) {
+		/* we have to take care about the case that only have one default branch,
+		 * expr result will not be unrefed in this case, but it should be like in ZEND_CASE */
+		zend_ast *stmt_ast = cases->child[0]->child[1];
+		if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) {
+			zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
+		} else if (expr_node.op_type == IS_CONST) {
+			zval_dtor(&expr_node.u.constant);
+		}
+
+		zend_begin_loop(ZEND_NOP, NULL);
+		zend_compile_stmt(stmt_ast);
+		zend_end_loop(get_next_op_number(CG(active_op_array)));
+		return;
+	}
+
 	zend_begin_loop(ZEND_FREE, &expr_node);
 
 	case_node.op_type = IS_TMP_VAR;
 	case_node.u.op.var = get_temporary_variable(CG(active_op_array));
 
+	jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0);
 	for (i = 0; i < cases->children; ++i) {
 		zend_ast *case_ast = cases->child[i];
 		zend_ast *cond_ast = case_ast->child[0];