From: Dmitry Stogov Date: Fri, 10 Jul 2015 01:13:34 +0000 (+0300) Subject: Exception thrown by "return" statement (from TMP destructors) shouldn't be caught... X-Git-Tag: php-7.1.1RC1~35^2~37 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=44f7348caa188d6c2b6228eaa622d0ef86b2647e;p=php Exception thrown by "return" statement (from TMP destructors) shouldn't be caught in the same function --- diff --git a/Zend/tests/try_finally_012.phpt b/Zend/tests/try_finally_012.phpt new file mode 100644 index 0000000000..32fec7ab29 --- /dev/null +++ b/Zend/tests/try_finally_012.phpt @@ -0,0 +1,32 @@ +--TEST-- +Try finally (exception in "return" statement) +--FILE-- + +--EXPECT-- +finally +exception in main + diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0927f0cd23..44807d359b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a3bc17f425..8e9cfe795b 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -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) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b94614e659..5ea7b8a1fa 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(); } @@ -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)) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 828babb98d..8a6918e7ef 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -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(); }