]> granicus.if.org Git - php/commitdiff
Exception thrown by "return" statement (from TMP destructors) shouldn't be caught...
authorDmitry Stogov <dmitry@zend.com>
Fri, 10 Jul 2015 01:13:34 +0000 (04:13 +0300)
committerDmitry Stogov <dmitry@zend.com>
Fri, 10 Jul 2015 01:13:34 +0000 (04:13 +0300)
Zend/tests/try_finally_012.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/Zend/tests/try_finally_012.phpt b/Zend/tests/try_finally_012.phpt
new file mode 100644 (file)
index 0000000..32fec7a
--- /dev/null
@@ -0,0 +1,32 @@
+--TEST--
+Try finally (exception in "return" statement)
+--FILE--
+<?php
+class A {
+       public $x = 1;
+       public $y = 2;
+       function __destruct() {
+               throw new Exception();
+       }
+}
+function foo() {
+       foreach(new A() as $a) {
+               try {
+                       return $a;
+               } catch (Exception $e) {
+                       echo "exception in foo\n";
+               } finally {
+                       echo "finally\n";
+               }
+       }
+}
+try {
+       foo();
+} catch (Exception $e) {
+       echo "exception in main\n";
+}
+?>
+--EXPECT--
+finally
+exception in main
+
index 0927f0cd2323a34a48f1840b1d9778c6f7599fe9..44807d359b9f8cf73118fee9dc202772ad649067 100644 (file)
@@ -3514,9 +3514,20 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
 }
 /* }}} */
 
-static void zend_free_foreach_and_switch_variables(void) /* {{{ */
+static void zend_free_foreach_and_switch_variables(uint32_t flags) /* {{{ */
 {
+       uint32_t start_op_number = get_next_op_number(CG(active_op_array));
+
        zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var);
+
+       if (flags) {
+               uint32_t end_op_number = get_next_op_number(CG(active_op_array));
+
+               while (start_op_number < end_op_number) {
+                       CG(active_op_array)->opcodes[start_op_number].extended_value |= flags;
+                       start_op_number++;
+               }
+       }
 }
 /* }}} */
 
@@ -3538,7 +3549,7 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
                zend_compile_expr(&expr_node, expr_ast);
        }
 
-       zend_free_foreach_and_switch_variables();
+       zend_free_foreach_and_switch_variables(ZEND_FREE_ON_RETURN);
 
        if (CG(context).in_finally) {
                opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
index a3bc17f4256dd828e58ef9941683c11213e090be..8e9cfe795bd27105fedad619534c3c3f390d1aac 100644 (file)
@@ -875,7 +875,9 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
 
 #define ZEND_FETCH_ARG_MASK         0x000fffff
 
-#define ZEND_MEMBER_FUNC_CALL  1<<0
+#define ZEND_FREE_ON_RETURN     (1<<0)
+
+#define ZEND_MEMBER_FUNC_CALL   (1<<0)
 
 #define ZEND_ARG_SEND_BY_REF (1<<0)
 #define ZEND_ARG_COMPILE_TIME_BOUND (1<<1)
index b94614e6598fc94128fffb626aba4df749b1d300..5ea7b8a1fad95ebfba211ac1f72d9f5666c755ce 100644 (file)
@@ -2610,10 +2610,13 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, ANY)
 
 ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
 {
+       zval *var;
        USE_OPLINE
 
        SAVE_OPLINE();
-       zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+       var = EX_VAR(opline->op1.var);
+       zval_ptr_dtor_nogc(var);
+       ZVAL_NULL(var);
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
@@ -2626,8 +2629,10 @@ ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
        var = EX_VAR(opline->op1.var);
        if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
                zend_hash_iterator_del(Z_FE_ITER_P(var));
+               Z_FE_ITER_P(var) = (uint32_t)-1;
        }
        zval_ptr_dtor_nogc(var);
+       ZVAL_NULL(var);
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
@@ -7099,6 +7104,18 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
                }
        }
 
+       if (catch_op_num) {
+               if ((EX(func)->op_array.opcodes[op_num].opcode == ZEND_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
+                || (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FE_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
+               ) {
+                       /* exceptions thrown because of TMP variable destruction on "return"
+                        * statement should't be caught in the same function.
+                        * See: Zend/tests/try_finally_012.phpt
+                        */
+                       catch_op_num = 0;
+               }
+       }
+
        i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
 
        if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
index 828babb98d7bda237d665bfd422e8ac89bf40a22..8a6918e7ef4ceddb76a8bf7abda90596e6d3c94c 100644 (file)
@@ -1500,6 +1500,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
                }
        }
 
+       if (catch_op_num) {
+               if ((EX(func)->op_array.opcodes[op_num].opcode == ZEND_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
+                || (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FE_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
+               ) {
+                       /* exceptions thrown because of TMP variable destruction on "return"
+                        * statement should't be caught in the same function.
+                        * See: Zend/tests/try_finally_012.phpt
+                        */
+                       catch_op_num = 0;
+               }
+       }
+
        i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
 
        if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
@@ -40189,10 +40201,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER(Z
 
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
+       zval *var;
        USE_OPLINE
 
        SAVE_OPLINE();
-       zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+       var = EX_VAR(opline->op1.var);
+       zval_ptr_dtor_nogc(var);
+       ZVAL_NULL(var);
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
@@ -40205,8 +40220,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZE
        var = EX_VAR(opline->op1.var);
        if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
                zend_hash_iterator_del(Z_FE_ITER_P(var));
+               Z_FE_ITER_P(var) = (uint32_t)-1;
        }
        zval_ptr_dtor_nogc(var);
+       ZVAL_NULL(var);
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }