diff --git a/Zend/tests/fibers/out-of-memory-in-fiber.phpt b/Zend/tests/fibers/out-of-memory-in-fiber.phpt new file mode 100644 index 00000000000..8c8d0475ecc --- /dev/null +++ b/Zend/tests/fibers/out-of-memory-in-fiber.phpt @@ -0,0 +1,25 @@ +--TEST-- +Out of Memory in a fiber +--INI-- +memory_limit=10K +--SKIPIF-- + +--FILE-- +start(); + +?> +--EXPECTF-- +Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %sout-of-memory-in-fiber.php on line %d diff --git a/Zend/tests/fibers/out-of-memory-in-nested-fiber.phpt b/Zend/tests/fibers/out-of-memory-in-nested-fiber.phpt new file mode 100644 index 00000000000..daf1978df04 --- /dev/null +++ b/Zend/tests/fibers/out-of-memory-in-nested-fiber.phpt @@ -0,0 +1,29 @@ +--TEST-- +Out of Memory in a nested fiber +--INI-- +memory_limit=10K +--SKIPIF-- + +--FILE-- +start(); +}); + +$fiber->start(); + +?> +--EXPECTF-- +Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %sout-of-memory-in-nested-fiber.php on line %d diff --git a/Zend/zend.c b/Zend/zend.c index e0ec9673ad0..d86158d7a97 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -775,7 +775,6 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ executor_globals->exception = NULL; executor_globals->objects_store.object_buckets = NULL; executor_globals->current_fiber = NULL; - executor_globals->fiber_error = NULL; #ifdef ZEND_WIN32 zend_get_windows_version_info(&executor_globals->windows_version_info); #endif @@ -1348,11 +1347,6 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_stack delayed_oplines_stack; int type = orig_type & E_ALL; - /* Fatal errors must be handled in {main} */ - if (type & E_FATAL_ERRORS && EG(current_fiber)) { - zend_error_suspend_fiber(orig_type, error_filename, error_lineno, message); - } - /* If we're executing a function during SCCP, count any warnings that may be emitted, * but don't perform any other error handling. */ if (EG(capture_warnings_during_sccp)) { diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 54c72cd4a66..2c10a267359 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -58,7 +58,7 @@ extern transfer_t jump_fcontext(fcontext_t to, void *vp); #define ZEND_FIBER_DEFAULT_PAGE_SIZE 4096 -#define ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, trace_num) do { \ +#define ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, trace_num, bailout) do { \ stack = EG(vm_stack); \ stack->top = EG(vm_stack_top); \ stack->end = EG(vm_stack_end); \ @@ -66,9 +66,10 @@ extern transfer_t jump_fcontext(fcontext_t to, void *vp); execute_data = EG(current_execute_data); \ error_reporting = EG(error_reporting); \ trace_num = EG(jit_trace_num); \ + bailout = EG(bailout); \ } while (0) -#define ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, trace_num) do { \ +#define ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, trace_num, bailout) do { \ EG(vm_stack) = stack; \ EG(vm_stack_top) = stack->top; \ EG(vm_stack_end) = stack->end; \ @@ -76,6 +77,7 @@ extern transfer_t jump_fcontext(fcontext_t to, void *vp); EG(current_execute_data) = execute_data; \ EG(error_reporting) = error_reporting; \ EG(jit_trace_num) = trace_num; \ + EG(bailout) = bailout; \ } while (0) #if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) @@ -241,12 +243,13 @@ static void zend_fiber_suspend(zend_fiber *fiber) zend_execute_data *execute_data; int error_reporting; uint32_t jit_trace_num; + JMP_BUF *bailout; - ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); + ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num, bailout); zend_fiber_suspend_context(&fiber->context); - ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); + ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num, bailout); } static void zend_fiber_switch_to(zend_fiber *fiber) @@ -257,12 +260,13 @@ static void zend_fiber_switch_to(zend_fiber *fiber) zend_execute_data *execute_data; int error_reporting; uint32_t jit_trace_num; + JMP_BUF *bailout; previous = EG(current_fiber); zend_observer_fiber_switch_notify(previous, fiber); - ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); + ZEND_FIBER_BACKUP_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num, bailout); EG(current_fiber) = fiber; @@ -270,40 +274,16 @@ static void zend_fiber_switch_to(zend_fiber *fiber) EG(current_fiber) = previous; - ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num); + ZEND_FIBER_RESTORE_EG(stack, stack_page_size, execute_data, error_reporting, jit_trace_num, bailout); zend_observer_fiber_switch_notify(fiber, previous); - if (UNEXPECTED(EG(fiber_error)) && fiber->status != ZEND_FIBER_STATUS_SHUTDOWN) { - if (previous) { - zend_fiber_suspend(previous); // Still in fiber, suspend again until in {main}. - abort(); // This fiber should never be resumed. - } - - zend_error_info *error = EG(fiber_error); - zend_error_zstr_at(error->type, error->filename, error->lineno, error->message); + if (UNEXPECTED(fiber->status == ZEND_FIBER_STATUS_BAILOUT)) { + // zend_bailout() was called in the fiber, so call it again in the previous fiber or {main}. + zend_bailout(); } } -ZEND_COLD void zend_error_suspend_fiber( - int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) -{ - ZEND_ASSERT(EG(current_fiber) && "Must be within an active fiber!"); - ZEND_ASSERT(orig_type & E_FATAL_ERRORS && "Error type must be fatal"); - - zend_error_info *error = emalloc(sizeof(zend_error_info)); - - error->type = orig_type; - error->filename = error_filename; - error->lineno = error_lineno; - error->message = message; - - EG(fiber_error) = error; - - zend_fiber_suspend(EG(current_fiber)); - - abort(); // This fiber should never be resumed. -} static zend_always_inline zend_vm_stack zend_fiber_vm_stack_alloc(size_t size) { @@ -348,21 +328,25 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context) fiber->status = ZEND_FIBER_STATUS_RUNNING; - zend_call_function(&fiber->fci, &fiber->fci_cache); + zend_first_try { + zend_call_function(&fiber->fci, &fiber->fci_cache); - zval_ptr_dtor(&fiber->fci.function_name); + zval_ptr_dtor(&fiber->fci.function_name); - if (EG(exception)) { - if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { - if (EXPECTED(zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception)))) { - zend_clear_exception(); + if (EG(exception)) { + if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { + if (EXPECTED(zend_is_graceful_exit(EG(exception)) || zend_is_unwind_exit(EG(exception)))) { + zend_clear_exception(); + } + } else { + fiber->status = ZEND_FIBER_STATUS_THREW; } } else { - fiber->status = ZEND_FIBER_STATUS_THREW; + fiber->status = ZEND_FIBER_STATUS_RETURNED; } - } else { - fiber->status = ZEND_FIBER_STATUS_RETURNED; - } + } zend_catch { + fiber->status = ZEND_FIBER_STATUS_BAILOUT; + } zend_end_try(); zend_vm_stack_destroy(); fiber->execute_data = NULL; @@ -512,13 +496,7 @@ ZEND_METHOD(Fiber, suspend) if (fiber->status == ZEND_FIBER_STATUS_SHUTDOWN) { // This occurs when the fiber is GC'ed while suspended. - if (EG(fiber_error)) { - // Throw UnwindExit so finally blocks are not executed on fatal error. - zend_throw_unwind_exit(); - } else { - // Otherwise throw GracefulExit to execute finally blocks. - zend_throw_graceful_exit(); - } + zend_throw_graceful_exit(); RETURN_THROWS(); } @@ -718,5 +696,4 @@ void zend_register_fiber_ce(void) void zend_fiber_init(void) { EG(current_fiber) = NULL; - EG(fiber_error) = NULL; } diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 28ae6ecc422..13ff4649b59 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -83,15 +83,13 @@ static const zend_uchar ZEND_FIBER_STATUS_RUNNING = 0x2; static const zend_uchar ZEND_FIBER_STATUS_RETURNED = 0x4; static const zend_uchar ZEND_FIBER_STATUS_THREW = 0x8; static const zend_uchar ZEND_FIBER_STATUS_SHUTDOWN = 0x10; +static const zend_uchar ZEND_FIBER_STATUS_BAILOUT = 0x20; -static const zend_uchar ZEND_FIBER_STATUS_FINISHED = 0x1c; +static const zend_uchar ZEND_FIBER_STATUS_FINISHED = 0x2c; ZEND_API zend_bool zend_fiber_init_context(zend_fiber_context *context, zend_fiber_coroutine coroutine, size_t stack_size); ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context); -ZEND_COLD void zend_error_suspend_fiber( - int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message); - ZEND_API void zend_fiber_switch_context(zend_fiber_context *to); ZEND_API void zend_fiber_suspend_context(zend_fiber_context *current); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 4a40a2158ff..41dc114d813 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -257,9 +257,6 @@ struct _zend_executor_globals { /* Default fiber C stack size. */ zend_long fiber_stack_size; - /* Pointer to fatal error that occurred in a fiber while switching to {main}. */ - zend_error_info *fiber_error; - /* If record_errors is enabled, all emitted diagnostics will be recorded, * in addition to being processed as usual. */ bool record_errors;