From: Nikita Popov Date: Sat, 28 May 2016 12:38:11 +0000 (+0200) Subject: Fix bug #71604 X-Git-Tag: php-7.1.0alpha1~76 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=921b3251b36eb5a930eb3de3496215833d3d548f;p=php Fix bug #71604 Alternatively could throw some kind of uncatchable dummy exception into the generator. Right now just checking for NULL in two places seems simpler. --- diff --git a/NEWS b/NEWS index 0c65ee8036..2330dfdc50 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,8 @@ PHP NEWS . Fixed bug #62814 (It is possible to stiffen child class members visibility). (Nikita) . Fixed bug #69989 (Generators don't participate in cycle GC). (Nikita) + . Fixed bug #71604 (Aborted Generators continue after nested finally). + (Nikita) . Fixed bug #71572 (String offset assignment from an empty string inserts null byte). (Francois) . Fixed bug #71897 (ASCII 0x7F Delete control character permitted in diff --git a/Zend/tests/try/bug71604.phpt b/Zend/tests/try/bug71604.phpt new file mode 100644 index 0000000000..79803b93ea --- /dev/null +++ b/Zend/tests/try/bug71604.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #71604: Aborted Generators continue after nested finally +--FILE-- +current(); + +?> +--EXPECT-- +INNER +OUTER diff --git a/Zend/tests/try/bug71604_2.phpt b/Zend/tests/try/bug71604_2.phpt new file mode 100644 index 0000000000..8736cd8347 --- /dev/null +++ b/Zend/tests/try/bug71604_2.phpt @@ -0,0 +1,39 @@ +--TEST-- +Bug #71604: Aborted Generators continue after nested finally (2) +--FILE-- +rewind(); +} catch (Exception $e) { + echo $e, "\n"; +} + +?> +--EXPECTF-- +Exception: 1 in %s:%d +Stack trace: +#0 [internal function]: gen() +#1 %s(%d): Generator->rewind() +#2 {main} + +Next Exception: 2 in %s:%d +Stack trace: +#0 [internal function]: gen() +#1 %s(%d): Generator->rewind() +#2 {main} diff --git a/Zend/tests/try/bug71604_3.phpt b/Zend/tests/try/bug71604_3.phpt new file mode 100644 index 0000000000..058c9a70a8 --- /dev/null +++ b/Zend/tests/try/bug71604_3.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #71604: Aborted Generators continue after nested finally (3) +--FILE-- +rewind(); +} catch (Exception $e) { + echo $e, "\n"; +} + +?> +--EXPECTF-- +Exception: 1 in %s:%d +Stack trace: +#0 [internal function]: gen() +#1 %s(%d): Generator->rewind() +#2 {main} + +Next Exception: 2 in %s:%d +Stack trace: +#0 %s(%d): gen() +#1 {main} diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b7a044f537..c8eb4cba4c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7107,6 +7107,7 @@ ZEND_VM_HANDLER(155, ZEND_BIND_TRAITS, ANY, ANY) ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_catch_offset, uint32_t op_num) { + /* May be NULL during generator closing (only finally blocks are executed) */ zend_object *ex = EG(exception); /* Walk try/catch/finally structures upwards, performing the necessary actions */ @@ -7114,7 +7115,7 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[try_catch_offset]; - if (op_num < try_catch->catch_op) { + if (op_num < try_catch->catch_op && ex) { /* Go to catch block */ cleanup_live_vars(execute_data, op_num, try_catch->catch_op); ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]); @@ -7134,7 +7135,11 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca /* Chain potential exception from wrapping finally block */ zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var); if (Z_OBJ_P(fast_call)) { - zend_exception_set_previous(ex, Z_OBJ_P(fast_call)); + if (ex) { + zend_exception_set_previous(ex, Z_OBJ_P(fast_call)); + } else { + EG(exception) = Z_OBJ_P(fast_call); + } ex = Z_OBJ_P(fast_call); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index d190cadd6a..2b9727acd2 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1692,6 +1692,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_TRAITS_SPEC_HANDLER(ZEND_ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_helper_SPEC(uint32_t try_catch_offset, uint32_t op_num ZEND_OPCODE_HANDLER_ARGS_DC) { + /* May be NULL during generator closing (only finally blocks are executed) */ zend_object *ex = EG(exception); /* Walk try/catch/finally structures upwards, performing the necessary actions */ @@ -1699,7 +1700,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_hel zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[try_catch_offset]; - if (op_num < try_catch->catch_op) { + if (op_num < try_catch->catch_op && ex) { /* Go to catch block */ cleanup_live_vars(execute_data, op_num, try_catch->catch_op); ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]); @@ -1719,7 +1720,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_hel /* Chain potential exception from wrapping finally block */ zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var); if (Z_OBJ_P(fast_call)) { - zend_exception_set_previous(ex, Z_OBJ_P(fast_call)); + if (ex) { + zend_exception_set_previous(ex, Z_OBJ_P(fast_call)); + } else { + EG(exception) = Z_OBJ_P(fast_call); + } ex = Z_OBJ_P(fast_call); } }