--- /dev/null
+--TEST--
+Exception handler should not be invoked for exit()
+--FILE--
+<?php
+
+set_exception_handler(function($e) {
+ var_dump($e);
+});
+
+exit("Exit\n");
+
+?>
+--EXPECT--
+Exit
--- /dev/null
+--TEST--
+exit() and finally (1)
+--FILE--
+<?php
+
+// TODO: In the future, we should execute the finally block.
+
+try {
+ exit("Exit\n");
+} catch (Throwable $e) {
+ echo "Not caught\n";
+} finally {
+ echo "Finally\n";
+}
+echo "Not executed\n";
+
+?>
+--EXPECT--
+Exit
--- /dev/null
+--TEST--
+exit() and finally (2)
+--FILE--
+<?php
+
+// TODO: In the future, we should execute the finally block.
+
+try {
+ try {
+ exit("Exit\n");
+ } catch (Throwable $e) {
+ echo "Not caught\n";
+ } finally {
+ throw new Exception("Finally exception");
+ }
+ echo "Not executed\n";
+} catch (Exception $e) {
+ echo "Caught {$e->getMessage()}\n";
+}
+
+?>
+--EXPECT--
+Exit
--- /dev/null
+--TEST--
+exit() and finally (3)
+--FILE--
+<?php
+
+// TODO: In the future, we should execute the finally block.
+
+function test() {
+ try {
+ exit("Exit\n");
+ } finally {
+ return 42;
+ }
+}
+var_dump(test());
+
+?>
+--EXPECT--
+Exit
zval orig_user_exception_handler;
zval params[1], retval2;
zend_object *old_exception;
+
+ if (zend_is_unwind_exit(EG(exception))) {
+ return;
+ }
+
old_exception = EG(exception);
EG(exception) = NULL;
ZVAL_OBJ(¶ms[0], old_exception);
zend_user_exception_handler();
}
if (EG(exception)) {
- zend_exception_error(EG(exception), E_ERROR);
- ret = FAILURE;
+ ret = zend_exception_error(EG(exception), E_ERROR);
}
}
destroy_op_array(op_array);
ZEND_API zend_class_entry *zend_ce_arithmetic_error;
ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
+/* Internal pseudo-exception that is not exposed to userland. */
+static zend_class_entry zend_ce_unwind_exit;
+
ZEND_API void (*zend_throw_exception_hook)(zval *ex);
static zend_object_handlers default_exception_handlers;
if (exception == add_previous || !add_previous || !exception) {
return;
}
+
+ if (zend_is_unwind_exit(add_previous)) {
+ OBJ_RELEASE(add_previous);
+ return;
+ }
+
ZVAL_OBJ(&pv, add_previous);
if (!instanceof_function(Z_OBJCE(pv), zend_ce_throwable)) {
zend_error_noreturn(E_CORE_ERROR, "Previous exception must implement Throwable");
INIT_CLASS_ENTRY(ce, "DivisionByZeroError", class_DivisionByZeroError_methods);
zend_ce_division_by_zero_error = zend_register_internal_class_ex(&ce, zend_ce_arithmetic_error);
zend_ce_division_by_zero_error->create_object = zend_default_exception_new;
+
+ INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL);
}
/* }}} */
/* }}} */
/* This function doesn't return if it uses E_ERROR */
-ZEND_API ZEND_COLD void zend_exception_error(zend_object *ex, int severity) /* {{{ */
+ZEND_API ZEND_COLD int zend_exception_error(zend_object *ex, int severity) /* {{{ */
{
zval exception, rv;
zend_class_entry *ce_exception;
+ int result = FAILURE;
ZVAL_OBJ(&exception, ex);
ce_exception = ex->ce;
zend_string_release_ex(str, 0);
zend_string_release_ex(file, 0);
+ } else if (ce_exception == &zend_ce_unwind_exit) {
+ /* We successfully unwound, nothing more to do */
+ result = SUCCESS;
} else {
zend_error(severity, "Uncaught exception '%s'", ZSTR_VAL(ce_exception->name));
}
OBJ_RELEASE(ex);
+ return result;
}
/* }}} */
zend_throw_exception_internal(exception);
}
/* }}} */
+
+ZEND_API ZEND_COLD void zend_throw_unwind_exit()
+{
+ ZEND_ASSERT(!EG(exception));
+ EG(exception) = zend_objects_new(&zend_ce_unwind_exit);
+ EG(opline_before_exception) = EG(current_execute_data)->opline;
+ EG(current_execute_data)->opline = EG(exception_op);
+}
+
+ZEND_API zend_bool zend_is_unwind_exit(zend_object *ex)
+{
+ return ex->ce == &zend_ce_unwind_exit;
+}
extern ZEND_API void (*zend_throw_exception_hook)(zval *ex);
/* show an exception using zend_error(severity,...), severity should be E_ERROR */
-ZEND_API ZEND_COLD void zend_exception_error(zend_object *exception, int severity);
+ZEND_API ZEND_COLD int zend_exception_error(zend_object *exception, int severity);
+
+ZEND_API ZEND_COLD void zend_throw_unwind_exit(void);
+ZEND_API zend_bool zend_is_unwind_exit(zend_object *ex);
#include "zend_globals.h"
result = zend_eval_stringl(str, str_len, retval_ptr, string_name);
if (handle_exceptions && EG(exception)) {
- zend_exception_error(EG(exception), E_ERROR);
- result = FAILURE;
+ result = zend_exception_error(EG(exception), E_ERROR);
}
return result;
}
} while (0);
FREE_OP1();
}
- zend_bailout();
- ZEND_VM_NEXT_OPCODE(); /* Never reached */
+ zend_throw_unwind_exit();
+ HANDLE_EXCEPTION();
}
ZEND_VM_HANDLER(57, ZEND_BEGIN_SILENCE, ANY, ANY)
zend_object *ex = EG(exception);
/* Walk try/catch/finally structures upwards, performing the necessary actions */
- while (try_catch_offset != (uint32_t) -1) {
+ for (; try_catch_offset != (uint32_t) -1; try_catch_offset--) {
zend_try_catch_element *try_catch =
&EX(func)->op_array.try_catch_array[try_catch_offset];
ZEND_VM_JMP_EX(&EX(func)->op_array.opcodes[try_catch->catch_op], 0);
} else if (op_num < try_catch->finally_op) {
+ if (ex && zend_is_unwind_exit(ex)) {
+ /* Don't execute finally blocks on exit (for now) */
+ continue;
+ }
+
/* Go to finally block */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
cleanup_live_vars(execute_data, op_num, try_catch->finally_op);
ex = Z_OBJ_P(fast_call);
}
}
-
- try_catch_offset--;
}
/* Uncaught exception */
} while (0);
FREE_OP(opline->op1_type, opline->op1.var);
}
- zend_bailout();
- ZEND_VM_NEXT_OPCODE(); /* Never reached */
+ zend_throw_unwind_exit();
+ HANDLE_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BEGIN_SILENCE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
/* May be NULL during generator closing (only finally blocks are executed) */
zend_object *ex = EG(exception);
+ zend_bool is_unwind_exit = ex && zend_is_unwind_exit(ex);
/* Walk try/catch/finally structures upwards, performing the necessary actions */
- while (try_catch_offset != (uint32_t) -1) {
+ for (; try_catch_offset != (uint32_t) -1; try_catch_offset--) {
zend_try_catch_element *try_catch =
&EX(func)->op_array.try_catch_array[try_catch_offset];
ZEND_VM_JMP_EX(&EX(func)->op_array.opcodes[try_catch->catch_op], 0);
} else if (op_num < try_catch->finally_op) {
+ if (is_unwind_exit) {
+ /* Don't execute finally blocks on exit (for now) */
+ continue;
+ }
+
/* Go to finally block */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
cleanup_live_vars(execute_data, op_num, try_catch->finally_op);
ex = Z_OBJ_P(fast_call);
}
}
-
- try_catch_offset--;
}
/* Uncaught exception */
--SKIPIF--
<?php
include 'skipif.inc';
-if (getenv('SKIP_ASAN')) die('skip some curl versions leak on longjmp');
?>
--FILE--
<?php
--SKIPIF--
<?php
include 'skipif.inc';
-if (getenv('SKIP_ASAN')) die('skip some curl versions leak on longjmp');
?>
--FILE--
<?php
zend_user_exception_handler();
}
if (EG(exception)) {
- zend_exception_error(EG(exception), E_ERROR);
- CG(unclean_shutdown) = 1;
- ret = FAILURE;
+ ret = zend_exception_error(EG(exception), E_ERROR);
+ if (ret == FAILURE) {
+ CG(unclean_shutdown) = 1;
+ }
}
}
destroy_op_array(op_array);
session_write_close();
echo "um, hi\n";
-/*
- * write() calls die(). This results in calling session_flush() which calls
- * write() in request shutdown. The code is inside save handler still and
- * calls save close handler.
- *
- * Because session_write_close() fails by die(), write() is called twice.
- * close() is still called at request shutdown since session is active.
- */
-
?>
--EXPECT--
write: goodbye cruel world
-
-Warning: Unknown: Cannot call session save handler in a recursive manner in Unknown on line 0
-
-Warning: Unknown: Failed to write session data using user defined save handler. (session.save_path: ) in Unknown on line 0
-close: goodbye cruel world
xmlFreeDoc(doc_request);
if (EG(exception)) {
- php_output_discard();
- _soap_server_exception(service, function, ZEND_THIS);
+ if (!zend_is_unwind_exit(EG(exception))) {
+ php_output_discard();
+ _soap_server_exception(service, function, ZEND_THIS);
+ }
goto fail;
}
efree(fn_name);
if (EG(exception)) {
- php_output_discard();
- _soap_server_exception(service, function, ZEND_THIS);
- if (service->type == SOAP_CLASS) {
+ if (!zend_is_unwind_exit(EG(exception))) {
+ php_output_discard();
+ _soap_server_exception(service, function, ZEND_THIS);
+ if (service->type == SOAP_CLASS) {
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
- if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
+ if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
#else
- if (soap_obj) {
+ if (soap_obj) {
#endif
- zval_ptr_dtor(soap_obj);
+ zval_ptr_dtor(soap_obj);
+ }
}
}
goto fail;
--SKIPIF--
<?php
if (!extension_loaded('xsl')) die('skip xsl not loaded');
-if (getenv('SKIP_ASAN')) die('xfail bailing out across foreign C code');
?>
--FILE--
<?php
}
#endif
+ if (exception && zend_is_unwind_exit(exception)) {
+ /* Restore bailout based exit. */
+ zend_bailout();
+ }
+
if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
phpdbg_print_opline_ex(execute_data, 0);
goto next;