38# include <valgrind/valgrind.h>
41#ifdef ZEND_FIBER_UCONTEXT
50# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
51# define MAP_ANONYMOUS MAP_ANON
57# if !defined(MAP_STACK) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
63# define MAP_FAILED ((void * ) -1)
67#ifdef __SANITIZE_ADDRESS__
68# include <sanitizer/asan_interface.h>
69# include <sanitizer/common_interface_defs.h>
74# define SHSTK_ENABLED (__CET__ & 0x2)
75# define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL)
76# define __NR_map_shadow_stack 451
77# ifndef SHADOW_STACK_SET_TOKEN
78# define SHADOW_STACK_SET_TOKEN 0x1
88 unsigned int valgrind_stack_id;
91#ifdef __SANITIZE_ADDRESS__
92 const void *asan_pointer;
96#ifdef ZEND_FIBER_UCONTEXT
99#elif BOOST_CONTEXT_SHADOW_STACK
117#ifdef ZEND_CHECK_STACK_LIMIT
125 state->vm_stack =
EG(vm_stack);
126 state->vm_stack_top =
EG(vm_stack_top);
127 state->vm_stack_end =
EG(vm_stack_end);
128 state->vm_stack_page_size =
EG(vm_stack_page_size);
129 state->current_execute_data =
EG(current_execute_data);
131 state->jit_trace_num =
EG(jit_trace_num);
134#ifdef ZEND_CHECK_STACK_LIMIT
135 state->stack_base =
EG(stack_base);
136 state->stack_limit =
EG(stack_limit);
142 EG(vm_stack) =
state->vm_stack;
143 EG(vm_stack_top) =
state->vm_stack_top;
144 EG(vm_stack_end) =
state->vm_stack_end;
145 EG(vm_stack_page_size) =
state->vm_stack_page_size;
146 EG(current_execute_data) =
state->current_execute_data;
148 EG(jit_trace_num) =
state->jit_trace_num;
151#ifdef ZEND_CHECK_STACK_LIMIT
152 EG(stack_base) =
state->stack_base;
153 EG(stack_limit) =
state->stack_limit;
157#ifdef ZEND_FIBER_UCONTEXT
189#define ZEND_FIBER_DEFAULT_PAGE_SIZE 4096
191static size_t zend_fiber_get_page_size(
void)
193 static size_t page_size = 0;
197 if (!page_size || (page_size & (page_size - 1))) {
209 const size_t page_size = zend_fiber_get_page_size();
212 if (
size < minimum_stack_size) {
217 const size_t stack_size = (
size + page_size - 1) / page_size * page_size;
221 pointer = VirtualAlloc(0, alloc_size, MEM_COMMIT, PAGE_READWRITE);
231# if ZEND_FIBER_GUARD_PAGES
234 if (!VirtualProtect(pointer,
ZEND_FIBER_GUARD_PAGES * page_size, PAGE_READWRITE | PAGE_GUARD, &protect)) {
239 VirtualFree(pointer, 0, MEM_RELEASE);
251#if defined(MADV_NOHUGEPAGE)
254 (
void) madvise(pointer, alloc_size, MADV_NOHUGEPAGE);
257 zend_mmap_set_name(pointer, alloc_size,
"zend_fiber_stack");
259# if ZEND_FIBER_GUARD_PAGES
262 munmap(pointer, alloc_size);
271 stack->
size = stack_size;
273#if !defined(ZEND_FIBER_UCONTEXT) && BOOST_CONTEXT_SHADOW_STACK
275 stack->ss_size= stack_size >> 5;
278 stack->ss_size = (stack->ss_size + 7) & ~7;
282 stack->ss_base = (
void *)syscall(__NR_map_shadow_stack, 0, stack->ss_size, SHADOW_STACK_SET_TOKEN);
290#ifdef VALGRIND_STACK_REGISTER
291 uintptr_t base = (uintptr_t) stack->
pointer;
292 stack->valgrind_stack_id = VALGRIND_STACK_REGISTER(base, base + stack->
size);
295#ifdef __SANITIZE_ADDRESS__
296 stack->asan_pointer = stack->
pointer;
297 stack->asan_size = stack->
size;
305#ifdef VALGRIND_STACK_DEREGISTER
306 VALGRIND_STACK_DEREGISTER(stack->valgrind_stack_id);
309 const size_t page_size = zend_fiber_get_page_size();
313#ifdef __SANITIZE_ADDRESS__
320 VirtualFree(pointer, 0, MEM_RELEASE);
325#if !defined(ZEND_FIBER_UCONTEXT) && BOOST_CONTEXT_SHADOW_STACK
326 munmap(stack->ss_base, stack->ss_size);
332#ifdef ZEND_CHECK_STACK_LIMIT
343 reserve = reserve * 2;
347 return (int8_t*)stack->
pointer + reserve;
352 return (
void*)((uintptr_t)stack->
pointer + stack->
size);
356#ifdef ZEND_FIBER_UCONTEXT
363#ifdef ZEND_FIBER_UCONTEXT
371#ifdef __SANITIZE_ADDRESS__
372 __sanitizer_finish_switch_fiber(
NULL, &from->
stack->asan_pointer, &from->
stack->asan_size);
375#ifndef ZEND_FIBER_UCONTEXT
415 context->stack = zend_fiber_stack_allocate(stack_size);
421#ifdef ZEND_FIBER_UCONTEXT
428 handle->uc_stack.ss_flags = 0;
431 makecontext(
handle, (
void (*)(
void)) zend_fiber_trampoline, 0);
436 void *stack = (
void *) ((uintptr_t)
context->stack->pointer +
context->stack->size);
438#if BOOST_CONTEXT_SHADOW_STACK
442 *((
unsigned long*) (stack - 8)) = (
unsigned long)
context->stack->ss_base +
context->stack->ss_size;
468 zend_fiber_stack_free(
context->stack);
478 ZEND_ASSERT(from &&
"From fiber context must be present");
479 ZEND_ASSERT(to != from &&
"Cannot switch into the running fiber context");
489 ) &&
"Error transfer requires a throwable value");
493 zend_fiber_capture_vm_state(&
state);
504 EG(current_fiber_context) = to;
506#ifdef __SANITIZE_ADDRESS__
507 void *fake_stack =
NULL;
508 __sanitizer_start_switch_fiber(
510 to->
stack->asan_pointer,
511 to->
stack->asan_size);
514#ifdef ZEND_FIBER_UCONTEXT
515 transfer_data = transfer;
520 *transfer = *transfer_data;
525 *transfer = *
data.transfer;
530#ifndef ZEND_FIBER_UCONTEXT
535#ifdef __SANITIZE_ADDRESS__
536 __sanitizer_finish_switch_fiber(fake_stack, &to->
stack->asan_pointer, &to->
stack->asan_size);
539 EG(current_fiber_context) = from;
541 zend_fiber_restore_vm_state(&
state);
556 EG(vm_stack) = current_stack;
565 ZEND_ASSERT(!transfer->
flags &&
"No flags should be set on initial transfer");
580 EG(vm_stack) = stack;
582 EG(vm_stack_end) = stack->
end;
594 EG(jit_trace_num) = 0;
597#ifdef ZEND_CHECK_STACK_LIMIT
683 fiber->
caller =
EG(current_fiber_context);
704 return zend_fiber_switch_to(caller,
value,
false);
719 zend_fiber_delegate_transfer_result(&transfer,
EG(current_execute_data),
return_value);
732 zend_fiber_delegate_transfer_result(&transfer,
EG(current_execute_data),
return_value);
743 zend_fiber_delegate_transfer_result(&transfer,
EG(current_execute_data),
return_value);
752 zend_fiber_delegate_transfer_result(&transfer,
EG(current_execute_data),
return_value);
764static void zend_fiber_object_destroy(
zend_object *
object)
789 zend_rethrow_exception(
EG(current_execute_data));
794 if (!
EG(current_execute_data)) {
803static void zend_fiber_object_free(
zend_object *
object)
819 zend_get_gc_buffer_add_zval(
buf, &fiber->
result);
822 zend_get_gc_buffer_use(
buf, table, num);
828 for (;
ex;
ex =
ex->prev_execute_data) {
841 if (!(
generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) {
855 zend_get_gc_buffer_add_zval(
buf,
val);
858 lastSymTable = symTable;
862 zend_get_gc_buffer_use(
buf, table, num);
899 zend_throw_error(zend_ce_fiber_error,
"Cannot switch fibers in current execution context");
904 zend_throw_error(zend_ce_fiber_error,
"Cannot start a fiber that has already been started");
936 zend_throw_error(zend_ce_fiber_error,
"Cannot suspend in a force-closed fiber");
941 zend_throw_error(zend_ce_fiber_error,
"Cannot switch fibers in current execution context");
965 zend_throw_error(zend_ce_fiber_error,
"Cannot switch fibers in current execution context");
972 zend_throw_error(zend_ce_fiber_error,
"Cannot resume a fiber that is not suspended");
993 zend_throw_error(zend_ce_fiber_error,
"Cannot switch fibers in current execution context");
1000 zend_throw_error(zend_ce_fiber_error,
"Cannot resume a fiber that is not suspended");
1058 const char *message;
1066 message =
"The fiber threw an exception";
1068 message =
"The fiber exited with a fatal error";
1073 message =
"The fiber has not been started";
1075 message =
"The fiber has not returned";
1078 zend_throw_error(zend_ce_fiber_error,
"Cannot get fiber return value: %s", message);
1099 "The \"%s\" class is reserved for internal use and cannot be manually instantiated",
1109 zend_ce_fiber->default_object_handlers = &zend_fiber_handlers;
1112 zend_fiber_handlers.dtor_obj = zend_fiber_object_destroy;
1113 zend_fiber_handlers.free_obj = zend_fiber_object_free;
1114 zend_fiber_handlers.get_gc = zend_fiber_object_gc;
1115 zend_fiber_handlers.clone_obj =
NULL;
1117 zend_ce_fiber_error = register_class_FiberError(
zend_ce_error);
1118 zend_ce_fiber_error->create_object =
zend_ce_error->create_object;
1125#if defined(__SANITIZE_ADDRESS__) || defined(ZEND_FIBER_UCONTEXT)
1129#ifdef ZEND_FIBER_UCONTEXT
1145#if defined(__SANITIZE_ADDRESS__) || defined(ZEND_FIBER_UCONTEXT)
1146 efree(
EG(main_fiber_context)->stack);
1149 efree(
EG(main_fiber_context));
memset(ptr, 0, type->size)
zend_ffi_ctype_name_buf buf
zend_test_fiber * active_fiber
int mprotect(void *addr, size_t size, int protection)
zend_execute_data * prev_execute_data
zend_fiber_context * context
zend_execute_data * current_execute_data
size_t vm_stack_page_size
zend_fiber * active_fiber
zend_execute_data * stack_bottom
zend_fiber_context * caller
zend_fcall_info_cache fci_cache
zend_fiber_context context
zend_execute_data * execute_data
zend_fiber_context * previous
zend_fiber_transfer * transfer
PHP_WINUTIL_API char * php_win32_error_to_msg(HRESULT error)
PHP_WINUTIL_API void php_win32_error_msg_free(char *msg)
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
ZEND_API size_t zend_get_page_size(void)
#define INTERNAL_FUNCTION_PARAMETERS
#define INTERNAL_FUNCTION_PARAM_PASSTHRU
#define Z_PARAM_FUNC(dest_fci, dest_fcc)
#define RETURN_COPY_DEREF(zv)
struct _zend_fcall_info_cache zend_fcall_info_cache
#define ZEND_PARSE_PARAMETERS_END()
#define ZEND_PARSE_PARAMETERS_NONE()
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
#define RETURN_OBJ_COPY(r)
struct _zend_fcall_info zend_fcall_info
#define ZEND_METHOD(classname, name)
#define RETURN_COPY_VALUE(zv)
#define Z_PARAM_OBJECT_OF_CLASS(dest, _ce)
#define Z_PARAM_ZVAL(dest)
ZEND_API zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache)
#define Z_PARAM_VARIADIC_WITH_NAMED(dest, dest_num, dest_named)
#define ecalloc(nmemb, size)
error_reporting(?int $error_level=null)
#define ZEND_USER_CODE(type)
#define ZEND_INTERNAL_FUNCTION
#define ZEND_CALL_FRAME_SLOT
#define ZEND_CALL_GENERATOR
#define ZEND_CALL_INFO(call)
ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception)
ZEND_API bool zend_is_unwind_exit(const zend_object *ex)
ZEND_API bool zend_is_graceful_exit(const zend_object *ex)
ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severity)
ZEND_API ZEND_COLD zend_object * zend_create_graceful_exit(void)
ZEND_API void zend_clear_exception(void)
ZEND_API ZEND_COLD zend_object * zend_throw_exception_ex(zend_class_entry *exception_ce, zend_long code, const char *format,...)
ZEND_API zend_class_entry * zend_ce_throwable
void zend_exception_set_previous(zend_object *exception, zend_object *add_previous)
ZEND_API zend_class_entry * zend_ce_error
ZEND_API HashTable * zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield)
ZEND_API void zend_vm_stack_destroy(void)
ZEND_API void(ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data)
ZEND_API zend_result zend_fiber_init_context(zend_fiber_context *context, void *kind, zend_fiber_coroutine coroutine, size_t stack_size)
void zend_register_fiber_ce(void)
#define ZEND_FIBER_DEFAULT_PAGE_SIZE
ZEND_API void zend_fiber_resume(zend_fiber *fiber, zval *value, zval *return_value)
ZEND_INDIRECT_RETURN boost_context_data jump_fcontext(void *to, zend_fiber_transfer *transfer)
ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer)
void * make_fcontext(void *sp, size_t size, void(*fn)(boost_context_data))
ZEND_API zend_class_entry * zend_ce_fiber
struct _zend_fiber_vm_state zend_fiber_vm_state
ZEND_API bool zend_fiber_switch_blocked(void)
ZEND_API void zend_fiber_suspend(zend_fiber *fiber, zval *value, zval *return_value)
ZEND_API void zend_fiber_switch_unblock(void)
ZEND_TLS uint32_t zend_fiber_switch_blocking
void zend_fiber_init(void)
ZEND_API void zend_fiber_switch_block(void)
void zend_fiber_shutdown(void)
ZEND_API zend_result zend_fiber_start(zend_fiber *fiber, zval *return_value)
ZEND_API void zend_fiber_resume_exception(zend_fiber *fiber, zval *exception, zval *return_value)
ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context)
#define ZEND_FIBER_GUARD_PAGES
@ ZEND_FIBER_TRANSFER_FLAG_BAILOUT
@ ZEND_FIBER_TRANSFER_FLAG_ERROR
struct _zend_fiber_stack zend_fiber_stack
@ ZEND_FIBER_FLAG_DESTROYED
@ ZEND_FIBER_FLAG_BAILOUT
@ ZEND_FIBER_STATUS_RUNNING
@ ZEND_FIBER_STATUS_SUSPENDED
void(* zend_fiber_coroutine)(zend_fiber_transfer *transfer)
struct _zend_fiber_transfer zend_fiber_transfer
#define ZEND_FIBER_VM_STACK_SIZE
union _zend_function zend_function
ZEND_API zend_get_gc_buffer * zend_get_gc_buffer_create(void)
HashTable * zend_generator_frame_gc(zend_get_gc_buffer *gc_buffer, zend_generator *generator)
struct _zend_generator zend_generator
struct _zend_fiber_context zend_fiber_context
struct _zend_vm_stack * zend_vm_stack
struct _zend_fiber zend_fiber
#define ZEND_HASH_FOREACH_END()
#define ZEND_HASH_FOREACH_VAL(ht, _val)
ZEND_API const zend_object_handlers std_object_handlers
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
ZEND_API void zend_object_std_dtor(zend_object *object)
ZEND_API void ZEND_FASTCALL zend_observer_fiber_switch_notify(zend_fiber_context *from, zend_fiber_context *to)
ZEND_API void ZEND_FASTCALL zend_observer_fiber_destroy_notify(zend_fiber_context *destroying)
ZEND_API void ZEND_FASTCALL zend_observer_fiber_init_notify(zend_fiber_context *initializing)
#define EXPECTED(condition)
#define zend_always_inline
#define ZEND_INDIRECT_RETURN
#define ZEND_STACK_ALIGNED
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
struct _zend_array HashTable
#define Z_OBJCE_P(zval_p)
#define Z_INDIRECT_P(zval_p)
#define ZVAL_OBJ_COPY(z, o)
ZEND_RESULT_CODE zend_result
struct _zend_object_handlers zend_object_handlers
struct _zend_execute_data zend_execute_data
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zend_generator * generator