]> granicus.if.org Git - php/commitdiff
Fixed bug #65784 (Segfault with finally).
authorXinchen Hui <laruence@gmail.com>
Thu, 12 Dec 2013 16:15:50 +0000 (00:15 +0800)
committerXinchen Hui <laruence@gmail.com>
Thu, 12 Dec 2013 16:16:08 +0000 (00:16 +0800)
NEWS
Zend/tests/bug65784.phpt [new file with mode: 0644]
Zend/tests/finally_goto_001.phpt [new file with mode: 0644]
Zend/tests/finally_goto_002.phpt [new file with mode: 0644]
Zend/tests/finally_goto_003.phpt [new file with mode: 0644]
Zend/tests/finally_goto_004.phpt [new file with mode: 0644]
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_opcode.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/NEWS b/NEWS
index dabe0cf1370706632dea94bcdf0dee6118f33b06..201e4209f27868514a098e3851b235ec581a8883 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,7 @@ PHP                                                                        NEWS
     emalloc/efree/estrdup (Anatol, Dmitry)
   . Implemented constant scalar expressions (with support for constants)
     (RFC: https://wiki.php.net/rfc/const_scalar_exprs). (Bob)
+  . Fixed bug #65784 (Segfault with finally). (Laruence, Dmitry)
 
 - cURL:
   . Implemented FR #65646 (re-enable CURLOPT_FOLLOWLOCATION with open_basedir
diff --git a/Zend/tests/bug65784.phpt b/Zend/tests/bug65784.phpt
new file mode 100644 (file)
index 0000000..29f086b
--- /dev/null
@@ -0,0 +1,60 @@
+--TEST--
+Fixed Bug #65784 (Segfault with finally)
+--FILE--
+<?php
+function foo1() {
+       try {
+               throw new Exception("not catch");
+               return true;
+       } finally {
+               try {
+                       throw new Exception("catched");
+               } catch (Exception $e) {
+               }
+       }
+}
+try {
+       $foo = foo1();
+       var_dump($foo);
+} catch (Exception $e) {
+       do {
+               var_dump($e->getMessage());
+       } while ($e = $e->getPrevious());
+}
+
+function foo2() {
+       try  {
+               try {
+                       throw new Exception("catched");
+                       return true;
+               } finally {
+                       try {
+                               throw new Exception("catched");
+                       } catch (Exception $e) {
+                       }
+               }
+       } catch (Exception $e) {
+       }
+}
+
+$foo = foo2();
+var_dump($foo);
+
+function foo3() {
+       try {
+               throw new Exception("not catched");
+               return true;
+       } finally {
+               try {
+                       throw new NotExists();
+               } catch (Exception $e) {
+               }
+       }
+}
+
+$bar = foo3();
+--EXPECTF--
+string(9) "not catch"
+NULL
+
+Fatal error: Class 'NotExists' not found in %sbug65784.php on line %d
diff --git a/Zend/tests/finally_goto_001.phpt b/Zend/tests/finally_goto_001.phpt
new file mode 100644 (file)
index 0000000..990f78d
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+jmp into a finally block 01
+--FILE--
+<?php
+function foo() {
+       goto test;
+       try {
+    } finally {
+test:
+    }
+}
+?>
+--EXPECTF--
+Fatal error: jump into a finally block is disallowed in %sfinally_goto_001.php on line %d
diff --git a/Zend/tests/finally_goto_002.phpt b/Zend/tests/finally_goto_002.phpt
new file mode 100644 (file)
index 0000000..a6bd9e3
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+jmp into a finally block 02
+--FILE--
+<?php
+function foo() {
+       try {
+               goto test;
+    } finally {
+test:
+    }
+}
+?>
+--EXPECTF--
+Fatal error: jump into a finally block is disallowed in %sfinally_goto_002.php on line %d
diff --git a/Zend/tests/finally_goto_003.phpt b/Zend/tests/finally_goto_003.phpt
new file mode 100644 (file)
index 0000000..8529ff7
--- /dev/null
@@ -0,0 +1,15 @@
+--TEST--
+jmp into a finally block 03
+--FILE--
+<?php
+function foo() {
+       try {
+    } finally {
+       goto test;
+test:
+    }
+}
+echo "okey";
+?>
+--EXPECTF--
+okey
diff --git a/Zend/tests/finally_goto_004.phpt b/Zend/tests/finally_goto_004.phpt
new file mode 100644 (file)
index 0000000..d88ceed
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+jmp into a finally block 03
+--FILE--
+<?php
+function foo() {
+       try {
+    } finally {
+test:
+    }
+       goto test;
+}
+?>
+--EXPECTF--
+Fatal error: jump into a finally block is disallowed in %sfinally_goto_004.php on line %d
index e3f06a001270446349d9754d36cdb73fb53c90ef..5d89a2b5c0dd5d683fc92b39984de5959359322e 100644 (file)
@@ -401,6 +401,7 @@ struct _zend_execute_data {
        zend_class_entry *current_called_scope;
        zval *current_this;
        struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
+       zval *delayed_exception;
        call_slot *call_slots;
        call_slot *call;
 };
index 5aea39ea9454135d90434bdff699e010ce726b9f..5c6d3e780bc2a5a357f148ffad6152a5b292d3b2 100644 (file)
@@ -1650,6 +1650,7 @@ static zend_always_inline zend_execute_data *i_create_execute_data_from_op_array
        EX(call) = NULL;
        EG(current_execute_data) = execute_data;
        EX(nested) = nested;
+       EX(delayed_exception) = NULL;
 
        if (!op_array->run_time_cache && op_array->last_cache_slot) {
                op_array->run_time_cache = ecalloc(op_array->last_cache_slot, sizeof(void*));
index ad0879888426c3217e53b4588b78786da574847a..89563ed47b019ca56ee19f25536293150766903c 100644 (file)
@@ -489,10 +489,15 @@ static void zend_check_finally_breakout(zend_op_array *op_array, zend_uint op_nu
        zend_uint i;
 
        for (i = 0; i < op_array->last_try_catch; i++) {
-               if (op_array->try_catch_array[i].try_op > op_num) {
-                       break;
-               }
-               if ((op_num >= op_array->try_catch_array[i].finally_op 
+               if ((op_num < op_array->try_catch_array[i].finally_op ||
+                                       op_num >= op_array->try_catch_array[i].finally_end)
+                               && (dst_num >= op_array->try_catch_array[i].finally_op &&
+                                        dst_num <= op_array->try_catch_array[i].finally_end)) {
+                       CG(in_compilation) = 1;
+                       CG(active_op_array) = op_array;
+                       CG(zend_lineno) = op_array->opcodes[op_num].lineno;
+                       zend_error(E_COMPILE_ERROR, "jump into a finally block is disallowed");
+               } else if ((op_num >= op_array->try_catch_array[i].finally_op 
                                        && op_num <= op_array->try_catch_array[i].finally_end)
                                && (dst_num > op_array->try_catch_array[i].finally_end 
                                        || dst_num < op_array->try_catch_array[i].finally_op)) {
@@ -541,11 +546,11 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num,
                        while (i > 0) {
                                i--;
                                if (op_array->try_catch_array[i].finally_op &&
-                                   op_num >= op_array->try_catch_array[i].try_op &&
-                                   op_num < op_array->try_catch_array[i].finally_op - 1 &&
-                                   (dst_num < op_array->try_catch_array[i].try_op ||
-                                    dst_num > op_array->try_catch_array[i].finally_end)) {
-                                       
+                                       op_num >= op_array->try_catch_array[i].try_op &&
+                                       op_num < op_array->try_catch_array[i].finally_op - 1 &&
+                                       (dst_num < op_array->try_catch_array[i].try_op ||
+                                        dst_num > op_array->try_catch_array[i].finally_end)) {
+
                                        opline = get_next_op(op_array TSRMLS_CC);
                                        opline->opcode = ZEND_FAST_CALL;
                                        SET_UNUSED(opline->op1);
@@ -565,7 +570,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num,
                        SET_UNUSED(opline->op2);
                        opline->op1.opline_num = start_op;
 
-                   break;
+                       break;
                }
        }       
 }
index f4224433ef4034a3ce5a7ccab371967395edde06..5385f3eec99c82e5de99f03510381ada09cf7914 100644 (file)
@@ -5021,7 +5021,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
 {
        zend_uint op_num = EG(opline_before_exception)-EG(active_op_array)->opcodes;
        int i;
-       zend_uint catch_op_num = 0, finally_op_num = 0;
+       zend_uint catch_op_num = 0, finally_op_num = 0, finally_op_end = 0;
        void **stack_frame;
 
        /* Figure out where the next stack frame (which maybe contains pushed
@@ -5046,6 +5046,10 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
                if (op_num < EG(active_op_array)->try_catch_array[i].finally_op) {
                        finally_op_num = EX(op_array)->try_catch_array[i].finally_op;
                }
+               if (op_num >= EG(active_op_array)->try_catch_array[i].finally_op &&
+                               op_num < EG(active_op_array)->try_catch_array[i].finally_end) {
+                       finally_op_end = EG(active_op_array)->try_catch_array[i].finally_end;
+               }
        }
 
        if (EX(call) >= EX(call_slots)) {
@@ -5107,14 +5111,29 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
        EX(old_error_reporting) = NULL;
 
        if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
-               zend_exception_save(TSRMLS_C);
+               if (EX(delayed_exception)) {
+                       zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
+               } 
+               EX(delayed_exception) = EG(exception);
+               EG(exception) = NULL;
                EX(fast_ret) = NULL;
                ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[finally_op_num]);
                ZEND_VM_CONTINUE();
        } else if (catch_op_num) {
+               if (finally_op_end && catch_op_num > finally_op_end) {
+                       /* we are going out of current finally scope */
+                       if (EX(delayed_exception)) {
+                               zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
+                               EX(delayed_exception) = NULL;
+                       }
+               }
                ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]);
                ZEND_VM_CONTINUE();
        } else {
+               if (EX(delayed_exception)) {
+                       zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
+                       EX(delayed_exception) = NULL;
+               }
                if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
                        ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN);
                } else {
@@ -5405,10 +5424,10 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE
 
 ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, ANY)
 {
-       if (EG(prev_exception) != NULL) {
+       if (EX(delayed_exception) != NULL) {
                /* discard the previously thrown exception */
-               zval_ptr_dtor(&EG(prev_exception));
-               EG(prev_exception) = NULL;
+               zval_ptr_dtor(&EX(delayed_exception));
+               EX(delayed_exception) = NULL;
        }
 
        ZEND_VM_NEXT_OPCODE();
@@ -5425,6 +5444,7 @@ ZEND_VM_HANDLER(162, ZEND_FAST_CALL, ANY, ANY)
                ZEND_VM_CONTINUE();
        }
        EX(fast_ret) = opline + 1;
+       EX(delayed_exception) = NULL;
        ZEND_VM_SET_OPCODE(opline->op1.jmp_addr);
        ZEND_VM_CONTINUE();
 }
@@ -5441,16 +5461,17 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, ANY)
                if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
                        ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
                        ZEND_VM_CONTINUE();
-               } else if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
-                       zend_exception_restore(TSRMLS_C);
-                       ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
-                       ZEND_VM_CONTINUE();
-               } else if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
-                       zend_exception_restore(TSRMLS_C);
-                       ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN);
                } else {
-                       zend_exception_restore(TSRMLS_C);
-                       ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
+                       EG(exception) = EX(delayed_exception);
+                       EX(delayed_exception) = NULL;
+                       if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
+                               ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
+                               ZEND_VM_CONTINUE();
+                       } else if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
+                               ZEND_VM_DISPATCH_TO_HANDLER(ZEND_GENERATOR_RETURN);
+                       } else {
+                               ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
+                       }
                }
        }
 }
index 77672a3d8634b521b9ee0342bc8d19fc9263233d..02f8acb6beb154a6371cf6f639f5a71ac1223198 100644 (file)
@@ -1016,7 +1016,7 @@ static int ZEND_FASTCALL  ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
 {
        zend_uint op_num = EG(opline_before_exception)-EG(active_op_array)->opcodes;
        int i;
-       zend_uint catch_op_num = 0, finally_op_num = 0;
+       zend_uint catch_op_num = 0, finally_op_num = 0, finally_op_end = 0;
        void **stack_frame;
 
        /* Figure out where the next stack frame (which maybe contains pushed
@@ -1041,6 +1041,10 @@ static int ZEND_FASTCALL  ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
                if (op_num < EG(active_op_array)->try_catch_array[i].finally_op) {
                        finally_op_num = EX(op_array)->try_catch_array[i].finally_op;
                }
+               if (op_num >= EG(active_op_array)->try_catch_array[i].finally_op &&
+                               op_num < EG(active_op_array)->try_catch_array[i].finally_end) {
+                       finally_op_end = EG(active_op_array)->try_catch_array[i].finally_end;
+               }
        }
 
        if (EX(call) >= EX(call_slots)) {
@@ -1102,14 +1106,29 @@ static int ZEND_FASTCALL  ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
        EX(old_error_reporting) = NULL;
 
        if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
-               zend_exception_save(TSRMLS_C);
+               if (EX(delayed_exception)) {
+                       zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
+               }
+               EX(delayed_exception) = EG(exception);
+               EG(exception) = NULL;
                EX(fast_ret) = NULL;
                ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[finally_op_num]);
                ZEND_VM_CONTINUE();
        } else if (catch_op_num) {
+               if (finally_op_end && catch_op_num > finally_op_end) {
+                       /* we are going out of current finally scope */
+                       if (EX(delayed_exception)) {
+                               zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
+                               EX(delayed_exception) = NULL;
+                       }
+               }
                ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]);
                ZEND_VM_CONTINUE();
        } else {
+               if (EX(delayed_exception)) {
+                       zend_exception_set_previous(EG(exception), EX(delayed_exception) TSRMLS_CC);
+                       EX(delayed_exception) = NULL;
+               }
                if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
                        return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
                } else {
@@ -1159,10 +1178,10 @@ static int ZEND_FASTCALL  ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS
 
 static int ZEND_FASTCALL  ZEND_DISCARD_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
-       if (EG(prev_exception) != NULL) {
+       if (EX(delayed_exception) != NULL) {
                /* discard the previously thrown exception */
-               zval_ptr_dtor(&EG(prev_exception));
-               EG(prev_exception) = NULL;
+               zval_ptr_dtor(&EX(delayed_exception));
+               EX(delayed_exception) = NULL;
        }
 
        ZEND_VM_NEXT_OPCODE();
@@ -1179,6 +1198,7 @@ static int ZEND_FASTCALL  ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
                ZEND_VM_CONTINUE();
        }
        EX(fast_ret) = opline + 1;
+       EX(delayed_exception) = NULL;
        ZEND_VM_SET_OPCODE(opline->op1.jmp_addr);
        ZEND_VM_CONTINUE();
 }
@@ -1195,16 +1215,17 @@ static int ZEND_FASTCALL  ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
                if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
                        ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
                        ZEND_VM_CONTINUE();
-               } else if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
-                       zend_exception_restore(TSRMLS_C);
-                       ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
-                       ZEND_VM_CONTINUE();
-               } else if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
-                       zend_exception_restore(TSRMLS_C);
-                       return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
                } else {
-                       zend_exception_restore(TSRMLS_C);
-                       return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+                       EG(exception) = EX(delayed_exception);
+                       EX(delayed_exception) = NULL;
+                       if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
+                               ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[opline->op2.opline_num]);
+                               ZEND_VM_CONTINUE();
+                       } else if (UNEXPECTED((EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0)) {
+                               return ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+                       } else {
+                               return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+                       }
                }
        }
 }