]> granicus.if.org Git - php/commitdiff
Fixed exception habdling on "return" statement.
authorDmitry Stogov <dmitry@zend.com>
Thu, 9 Jul 2015 17:47:25 +0000 (20:47 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 9 Jul 2015 17:47:25 +0000 (20:47 +0300)
Such exceptions shouldn't be caught in the same function.

Zend/tests/return_types/029.phpt
Zend/tests/return_types/030.phpt [new file with mode: 0644]
Zend/tests/temporary_cleaning_008.phpt [new file with mode: 0644]
Zend/tests/temporary_cleaning_009.phpt [new file with mode: 0644]
Zend/zend_opcode.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

index 37587452f5607862cceb461a4ad959639fd91b84..c854456a6b337eee5ad69c70367d3897ac61c259 100644 (file)
@@ -8,12 +8,15 @@ function foo(): string {
                try {
                        return $class; // invalid return type
                } catch (TypeError $e) {
-                       return "no leak or segfault";
+                       return "BAG!";
                }
        }
 }
-print foo();
-
+try {
+       print foo();
+} catch (TypeError $e) {
+       print "no leak or segfault";
+}
 ?>
 --EXPECT--
 no leak or segfault
diff --git a/Zend/tests/return_types/030.phpt b/Zend/tests/return_types/030.phpt
new file mode 100644 (file)
index 0000000..73b512e
--- /dev/null
@@ -0,0 +1,23 @@
+--TEST--
+Return types must not double free loop variables
+--FILE--
+<?php
+
+function foo(): string {
+       $a = [];
+       try {
+               return $a; // invalid return type
+       } catch (TypeError $e) {
+               echo "BAG!\n";
+               return "ops!\n";
+       }
+}
+try {
+       echo foo();
+} catch (TypeError $e) {
+       echo "OK\n";
+}
+
+?>
+--EXPECT--
+OK
diff --git a/Zend/tests/temporary_cleaning_008.phpt b/Zend/tests/temporary_cleaning_008.phpt
new file mode 100644 (file)
index 0000000..cbbbc6c
--- /dev/null
@@ -0,0 +1,42 @@
+--TEST--
+Exception inside a foreach loop with on an object with destructor
+--FILE--
+<?php
+class bar {
+       public $foo = 1;
+       public $bar = 2;
+       function __destruct() {
+               throw new Exception("test");
+       }
+}
+function foo(): string {
+       foreach (new bar() as $foo) {
+               try {
+                       $foo = new Exception;
+                       return;
+               } catch (Exception $e) {
+                       echo "Exception1: " . $e->getMessage() . "\n";
+               } catch (Error $e) {
+                       echo "Error1: " . $e->getMessage() . "\n";
+               }
+       }
+       echo "bag!\n";
+}
+try {
+       foo();
+} catch (Throwable $e) {
+       echo (($e instanceof Exception) ? "Exception2: " : "Error2: ") .
+               $e->getMessage() . "\n";
+       $e = $e->getPrevious();
+       while ($e instanceof Throwable) {
+               echo "\tPrev " . (($e instanceof Exception) ? "Exception2: " : "Error2: ") .
+                       $e->getMessage() . "\n";
+               $e = $e->getPrevious();         
+       }
+}
+echo "ok\n";
+?>
+--EXPECTF--
+Exception2: test
+       Prev Error2: Return value of foo() must be of the type string, none returned in %stemporary_cleaning_008.php on line %d
+ok
diff --git a/Zend/tests/temporary_cleaning_009.phpt b/Zend/tests/temporary_cleaning_009.phpt
new file mode 100644 (file)
index 0000000..c6f747e
--- /dev/null
@@ -0,0 +1,29 @@
+--TEST--
+Exception inside a foreach loop with on an object with destructor
+--FILE--
+<?php
+class bar {
+       public $foo = 1;
+       function __destruct() {
+               throw new Exception;
+       }
+}
+
+function foo() {
+       foreach (new bar() as &$foo) {
+               try {
+                       $foo = new Exception;
+                       return;
+               } catch (Exception $e) {
+                       echo "Exception1\n";
+               }
+       }
+}
+try {
+       foo();
+} catch (Exception $e) {
+       echo "Exception2\n";
+}
+?>
+--EXPECT--
+Exception2
index 64ba6562ffaac7e3205408c5013648fc31226cfd..e84edd54b92b9cf790a8a5d8306c47aee49241ca 100644 (file)
@@ -992,7 +992,8 @@ static zend_always_inline uint32_t *generate_var_liveliness_info_ex(zend_op_arra
                                && opline->opcode != ZEND_CASE
                                && opline->opcode != ZEND_FE_FETCH_R
                                && opline->opcode != ZEND_FE_FETCH_RW
-                               /* the following opcodes are not the "final" */
+                               /* the following opcodes are parts of "return" statement */
+                               && opline->opcode != ZEND_VERIFY_RETURN_TYPE
                                && (opline->opcode != ZEND_FREE || !(opline->extended_value & ZEND_FREE_ON_RETURN))
                                && (opline->opcode != ZEND_FE_FREE || !(opline->extended_value & ZEND_FREE_ON_RETURN))
                        ) {
index 8744bf315a0073052a5e6e7bd61bc5fd5eecc133..9b36b804c98d95fe0db3f336744b2e57877a9502 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();
 }
 
@@ -3883,6 +3888,9 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
 
                if (UNEXPECTED(EG(exception) != NULL)) {
                        FREE_OP1();
+                       if (OP1_TYPE == IS_TMP_VAR || OP1_TYPE == IS_VAR) {
+                               ZVAL_NULL(retval_ref);
+                       }
                }
 #endif
        }
@@ -7074,6 +7082,15 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
                }
        }
 
+       if (catch_op_num || finally_op_num) {
+               if (EX(func)->op_array.opcodes[op_num].opcode == ZEND_VERIFY_RETURN_TYPE
+                || (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))
+               ) {
+                       catch_op_num = finally_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 c45d2ee3c9a09db4d2e3e23b0910fc3808e02c43..6089b2e3c45ed0b1de76f5128f740a48816277f3 100644 (file)
@@ -1500,6 +1500,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
                }
        }
 
+       if (catch_op_num || finally_op_num) {
+               if (EX(func)->op_array.opcodes[op_num].opcode == ZEND_VERIFY_RETURN_TYPE
+                || (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))
+               ) {
+                       catch_op_num = finally_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)) {
@@ -7740,6 +7749,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_
 
                if (UNEXPECTED(EG(exception) != NULL)) {
 
+                       if (IS_CONST == IS_TMP_VAR || IS_CONST == IS_VAR) {
+                               ZVAL_NULL(retval_ref);
+                       }
                }
 #endif
        }
@@ -13536,6 +13548,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
 
                if (UNEXPECTED(EG(exception) != NULL)) {
                        zval_ptr_dtor_nogc(free_op1);
+                       if (IS_TMP_VAR == IS_TMP_VAR || IS_TMP_VAR == IS_VAR) {
+                               ZVAL_NULL(retval_ref);
+                       }
                }
 #endif
        }
@@ -19225,6 +19240,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
 
                if (UNEXPECTED(EG(exception) != NULL)) {
                        zval_ptr_dtor_nogc(free_op1);
+                       if (IS_VAR == IS_TMP_VAR || IS_VAR == IS_VAR) {
+                               ZVAL_NULL(retval_ref);
+                       }
                }
 #endif
        }
@@ -24910,6 +24928,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
 
                if (UNEXPECTED(EG(exception) != NULL)) {
 
+                       if (IS_UNUSED == IS_TMP_VAR || IS_UNUSED == IS_VAR) {
+                               ZVAL_NULL(retval_ref);
+                       }
                }
 #endif
        }
@@ -34346,6 +34367,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
 
                if (UNEXPECTED(EG(exception) != NULL)) {
 
+                       if (IS_CV == IS_TMP_VAR || IS_CV == IS_VAR) {
+                               ZVAL_NULL(retval_ref);
+                       }
                }
 #endif
        }
@@ -40164,10 +40188,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();
 }
 
@@ -40180,8 +40207,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();
 }