From 794a888a48715af5a97e3af9a8bdd88b20432f53 Mon Sep 17 00:00:00 2001
From: Xinchen Hui <laruence@gmail.com>
Date: Fri, 13 Dec 2013 00:15:50 +0800
Subject: [PATCH] Fixed bug #65784 (Segfault with finally).

--- Zend/zend_compile.h
+++ Zend/zend_compile.h
@@ -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;
 };
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 5aea39e..5c6d3e7 100644
--- Zend/zend_execute.c
+++ Zend/zend_execute.c
@@ -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*));
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index f422443..5385f3e 100644
--- Zend/zend_vm_def.h
+++ Zend/zend_vm_def.h
@@ -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);
+			}
 		}
 	}
 }
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 77672a3..02f8acb 100644
--- Zend/zend_vm_execute.h
+++ Zend/zend_vm_execute.h
@@ -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);
+			}
 		}
 	}
 }
-- 
2.0.3

