]> granicus.if.org Git - php/commitdiff
Fix bug #71604
authorNikita Popov <nikic@php.net>
Sat, 28 May 2016 12:38:11 +0000 (14:38 +0200)
committerNikita Popov <nikic@php.net>
Sat, 28 May 2016 12:40:32 +0000 (14:40 +0200)
Alternatively could throw some kind of uncatchable dummy exception
into the generator. Right now just checking for NULL in two places
seems simpler.

NEWS
Zend/tests/try/bug71604.phpt [new file with mode: 0644]
Zend/tests/try/bug71604_2.phpt [new file with mode: 0644]
Zend/tests/try/bug71604_3.phpt [new file with mode: 0644]
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/NEWS b/NEWS
index 0c65ee803665d790d26c4e07f5810a36e30461c1..2330dfdc50019873bba1caa19c4c2ba9359d0fac 100644 (file)
--- 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 (file)
index 0000000..79803b9
--- /dev/null
@@ -0,0 +1,25 @@
+--TEST--
+Bug #71604: Aborted Generators continue after nested finally
+--FILE--
+<?php
+function gen() {
+    try {
+        try {
+            yield;
+        } finally {
+            print "INNER\n";
+        }
+    } catch (Exception $e) {
+        print "EX\n";
+    } finally {
+        print "OUTER\n";
+    }
+    print "NOTREACHED\n";
+}
+
+gen()->current();
+
+?>
+--EXPECT--
+INNER
+OUTER
diff --git a/Zend/tests/try/bug71604_2.phpt b/Zend/tests/try/bug71604_2.phpt
new file mode 100644 (file)
index 0000000..8736cd8
--- /dev/null
@@ -0,0 +1,39 @@
+--TEST--
+Bug #71604: Aborted Generators continue after nested finally (2)
+--FILE--
+<?php
+
+function gen() {
+    try {
+        throw new Exception(1);
+    } finally {
+        try {
+            throw new Exception(2);
+        } finally {
+            try {
+                yield;
+            } finally {
+            }
+        }
+    }
+}
+
+try {
+    gen()->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 (file)
index 0000000..058c9a7
--- /dev/null
@@ -0,0 +1,38 @@
+--TEST--
+Bug #71604: Aborted Generators continue after nested finally (3)
+--FILE--
+<?php
+
+function gen() {
+    try {
+        throw new Exception(1);
+    } finally {
+        try {
+            yield;
+        } finally {
+            try {
+                throw new Exception(2);
+            } finally {
+            }
+        }
+    }
+}
+
+try {
+    gen()->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}
index b7a044f537381c5eb208d61b51c32ce43105c533..c8eb4cba4ce657f8abde34db1cc06b6683a1f6af 100644 (file)
@@ -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);
                        }
                }
index d190cadd6a28bf8668f6e0a68ece033df90cf213..2b9727acd2403dfb2dd5709f331c32eca8fd1c3b 100644 (file)
@@ -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);
                        }
                }