diff options
author | Dmitry Stogov <dmitry@zend.com> | 2015-07-09 20:47:25 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2015-07-09 20:47:25 +0300 |
commit | 17c5315bdf8f8087979aeb55f6d3a512ba197cf5 (patch) | |
tree | 7351d65a28af32587e436e73d25e8d87f3812b6c | |
parent | d91aad5966f01259f0e1a431a754d917807761b5 (diff) | |
download | php-git-17c5315bdf8f8087979aeb55f6d3a512ba197cf5.tar.gz |
Fixed exception habdling on "return" statement.
Such exceptions shouldn't be caught in the same function.
-rw-r--r-- | Zend/tests/return_types/029.phpt | 9 | ||||
-rw-r--r-- | Zend/tests/return_types/030.phpt | 23 | ||||
-rw-r--r-- | Zend/tests/temporary_cleaning_008.phpt | 42 | ||||
-rw-r--r-- | Zend/tests/temporary_cleaning_009.phpt | 29 | ||||
-rw-r--r-- | Zend/zend_opcode.c | 3 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 19 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 31 |
7 files changed, 150 insertions, 6 deletions
diff --git a/Zend/tests/return_types/029.phpt b/Zend/tests/return_types/029.phpt index 37587452f5..c854456a6b 100644 --- a/Zend/tests/return_types/029.phpt +++ b/Zend/tests/return_types/029.phpt @@ -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 index 0000000000..73b512ed14 --- /dev/null +++ b/Zend/tests/return_types/030.phpt @@ -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 index 0000000000..cbbbc6cd6c --- /dev/null +++ b/Zend/tests/temporary_cleaning_008.phpt @@ -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 index 0000000000..c6f747edb5 --- /dev/null +++ b/Zend/tests/temporary_cleaning_009.phpt @@ -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 diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 64ba6562ff..e84edd54b9 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -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)) ) { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 8744bf315a..9b36b804c9 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -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)) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index c45d2ee3c9..6089b2e3c4 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -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(); } |