diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 734494d252da0..6ae3e6bbb7bbc 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4480,6 +4480,7 @@ zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32 /* copy call frame into new stack segment */ new_call = zend_vm_stack_extend(used_stack * sizeof(zval)); + ZEND_UNPOISON_MEMORY_REGION(new_call, used_stack * sizeof(zval)); *new_call = *call; ZEND_ADD_CALL_FLAG(new_call, ZEND_CALL_ALLOCATED); @@ -5607,11 +5608,13 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame_ex(u if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) { EX(opline) = opline; /* this is the only difference */ call = (zend_execute_data*)zend_vm_stack_extend(used_stack); + ZEND_UNPOISON_MEMORY_REGION(call, used_stack); ZEND_ASSERT_VM_STACK_GLOBAL; zend_vm_init_call_frame(call, call_info | ZEND_CALL_ALLOCATED, func, num_args, object_or_called_scope); return call; } else { EG(vm_stack_top) = (zval*)((char*)call + used_stack); + ZEND_UNPOISON_MEMORY_REGION(call, used_stack); zend_vm_init_call_frame(call, call_info, func, num_args, object_or_called_scope); return call; } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index cf15c9e3b2db5..f09d1025055be 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -24,8 +24,10 @@ #include "zend_compile.h" #include "zend_hash.h" #include "zend_operators.h" +#include "zend_types.h" #include "zend_variables.h" #include "zend_constants.h" +#include "zend_sanitizers.h" #include @@ -322,6 +324,7 @@ static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend page->top = ZEND_VM_STACK_ELEMENTS(page); page->end = (zval*)((char*)page + size); page->prev = prev; + ZEND_POISON_MEMORY_REGION(page->top, (uintptr_t)page->end - (uintptr_t)page->top); return page; } @@ -342,11 +345,13 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(ui if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) { call = (zend_execute_data*)zend_vm_stack_extend(used_stack); + ZEND_UNPOISON_MEMORY_REGION(call, used_stack); ZEND_ASSERT_VM_STACK_GLOBAL; zend_vm_init_call_frame(call, call_info | ZEND_CALL_ALLOCATED, func, num_args, object_or_called_scope); return call; } else { EG(vm_stack_top) = (zval*)((char*)call + used_stack); + ZEND_UNPOISON_MEMORY_REGION(call, used_stack); zend_vm_init_call_frame(call, call_info, func, num_args, object_or_called_scope); return call; } @@ -370,6 +375,21 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint3 func, num_args, object_or_called_scope); } +static zend_always_inline zend_execute_data *zend_vm_stack_pop_call_frame(zend_execute_data *execute_data) +{ +#ifdef __SANITIZE_ADDRESS__ + zend_execute_data *prev_execute_data = execute_data->prev_execute_data; + + ZEND_POISON_MEMORY_REGION(execute_data, (uintptr_t)EG(vm_stack_top) - (uintptr_t)execute_data); + EG(vm_stack_top) = (zval*)execute_data; + + return prev_execute_data; +#else + EG(vm_stack_top) = (zval*)execute_data; + return execute_data->prev_execute_data; +#endif +} + static zend_always_inline void zend_vm_stack_free_extra_args_ex(uint32_t call_info, zend_execute_data *call) { if (UNEXPECTED(call_info & ZEND_CALL_FREE_EXTRA_ARGS)) { @@ -415,6 +435,7 @@ static zend_always_inline void zend_vm_stack_free_call_frame_ex(uint32_t call_in EG(vm_stack) = prev; efree(p); } else { + ZEND_POISON_MEMORY_REGION(call, (uintptr_t)EG(vm_stack_top) - (uintptr_t)call); EG(vm_stack_top) = (zval*)call; } @@ -433,6 +454,7 @@ static zend_always_inline void zend_vm_stack_extend_call_frame( zend_execute_data **call, uint32_t passed_args, uint32_t additional_args) { if (EXPECTED((uint32_t)(EG(vm_stack_end) - EG(vm_stack_top)) > additional_args)) { + ZEND_UNPOISON_MEMORY_REGION(EG(vm_stack_top), additional_args * sizeof(zval)); EG(vm_stack_top) += additional_args; } else { *call = zend_vm_stack_copy_call_frame(*call, passed_args, additional_args); diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 97b7cdcc911b7..26127a619d850 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -30,6 +30,7 @@ #include "zend_compile.h" #include "zend_closures.h" #include "zend_generators.h" +#include "zend_sanitizers.h" #include "zend_fibers.h" #include "zend_fibers_arginfo.h" @@ -583,6 +584,7 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) EG(vm_stack_page_size) = ZEND_FIBER_VM_STACK_SIZE; fiber->execute_data = (zend_execute_data *) stack->top; + ZEND_UNPOISON_MEMORY_REGION(stack->top, sizeof(zend_execute_data)); fiber->stack_bottom = fiber->execute_data; memset(fiber->execute_data, 0, sizeof(zend_execute_data)); diff --git a/Zend/zend_sanitizers.h b/Zend/zend_sanitizers.h new file mode 100644 index 0000000000000..6e19a4308d9c2 --- /dev/null +++ b/Zend/zend_sanitizers.h @@ -0,0 +1,52 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_SANITIZERS_H +#define ZEND_SANITIZERS_H + +#include "zend_portability.h" + +#ifdef __SANITIZE_ADDRESS__ +# include +#else +# define ASAN_POISON_MEMORY_REGION(_ptr, _size) +# define ASAN_UNPOISON_MEMORY_REGION(_ptr, _size) +#endif + +#if __has_feature(memory_sanitizer) +# include +# define MSAN_POISON_MEMORY_REGION(_ptr, _size) __msan_allocated_memory(_ptr, _size) +# define MSAN_UNPOISON_MEMORY_REGION(_ptr, _size) __msan_unpoison(_ptr, _size) +#else +# define MSAN_POISON_MEMORY_REGION(_ptr, _size) +# define MSAN_UNPOISON_MEMORY_REGION(_ptr, _size) +#endif + +/* Mark memory region as unaddressable (ASAN) and uninitialized (MSAN) */ +#define ZEND_POISON_MEMORY_REGION(_ptr, _size) do { \ + ZEND_ASSERT(!(((uintptr_t) (_ptr)) & 7)); \ + ASAN_POISON_MEMORY_REGION((_ptr), (_size)); \ + MSAN_POISON_MEMORY_REGION((_ptr), (_size)); \ +} while (0); + +/* Mark memory region as addressable (ASAN) without changing initialization state (MSAN) */ +#define ZEND_UNPOISON_MEMORY_REGION(_ptr, _size) do { \ + ZEND_ASSERT(!(((uintptr_t) (_ptr)) & 7)); \ + ASAN_UNPOISON_MEMORY_REGION((_ptr), (_size)); \ + /* No MSAN_UNPOISON_MEMORY_REGION */ \ +} while (0); + +#endif /* ZEND_SANITIZERS_H */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d305b0a9516ba..0421eb3d7e7fe 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2976,8 +2976,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } - EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = zend_vm_stack_pop_call_frame(execute_data); if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); @@ -4145,7 +4144,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) } zend_vm_stack_free_call_frame_ex(call_info, call); } else { - EG(vm_stack_top) = (zval*)call; + zend_vm_stack_pop_call_frame(call); } if (!RETURN_VALUE_USED(opline)) { @@ -4281,7 +4280,7 @@ ZEND_VM_C_LABEL(fcall_by_name_end): } zend_vm_stack_free_call_frame_ex(call_info, call); } else { - EG(vm_stack_top) = (zval*)call; + zend_vm_stack_pop_call_frame(call); } if (!RETURN_VALUE_USED(opline)) { @@ -4701,8 +4700,7 @@ ZEND_VM_HANDLER(139, ZEND_GENERATOR_CREATE, ANY, ANY) call_info = EX_CALL_INFO(); EG(current_execute_data) = EX(prev_execute_data); if (EXPECTED(!(call_info & (ZEND_CALL_TOP|ZEND_CALL_ALLOCATED)))) { - EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = zend_vm_stack_pop_call_frame(execute_data); LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED(!(call_info & ZEND_CALL_TOP))) { @@ -6162,7 +6160,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { if (c->ce->type == ZEND_USER_CLASS) { /* Recursion protection only applied to user constants, GH-18463 */ CONST_PROTECT_RECURSION(c); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 677166304f506..2b3b009142050 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1192,8 +1192,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } - EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = zend_vm_stack_pop_call_frame(execute_data); if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); @@ -1368,7 +1367,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV } zend_vm_stack_free_call_frame_ex(call_info, call); } else { - EG(vm_stack_top) = (zval*)call; + zend_vm_stack_pop_call_frame(call); } if (!0) { @@ -1432,7 +1431,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV } zend_vm_stack_free_call_frame_ex(call_info, call); } else { - EG(vm_stack_top) = (zval*)call; + zend_vm_stack_pop_call_frame(call); } if (!1) { @@ -1497,7 +1496,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_OBS } zend_vm_stack_free_call_frame_ex(call_info, call); } else { - EG(vm_stack_top) = (zval*)call; + zend_vm_stack_pop_call_frame(call); } if (!RETURN_VALUE_USED(opline)) { @@ -1679,7 +1678,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S } zend_vm_stack_free_call_frame_ex(call_info, call); } else { - EG(vm_stack_top) = (zval*)call; + zend_vm_stack_pop_call_frame(call); } if (!0) { @@ -1788,7 +1787,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S } zend_vm_stack_free_call_frame_ex(call_info, call); } else { - EG(vm_stack_top) = (zval*)call; + zend_vm_stack_pop_call_frame(call); } if (!1) { @@ -1899,7 +1898,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_ } zend_vm_stack_free_call_frame_ex(call_info, call); } else { - EG(vm_stack_top) = (zval*)call; + zend_vm_stack_pop_call_frame(call); } if (!RETURN_VALUE_USED(opline)) { @@ -2351,8 +2350,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_CREATE_SPEC_HANDLER( call_info = EX_CALL_INFO(); EG(current_execute_data) = EX(prev_execute_data); if (EXPECTED(!(call_info & (ZEND_CALL_TOP|ZEND_CALL_ALLOCATED)))) { - EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = zend_vm_stack_pop_call_frame(execute_data); LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED(!(call_info & ZEND_CALL_TOP))) { @@ -58799,8 +58797,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } - EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = zend_vm_stack_pop_call_frame(execute_data); if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index b20afffa47df0..fb9d34c3246b0 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -17,6 +17,7 @@ */ #include "Zend/zend_API.h" +#include "Zend/zend_sanitizers.h" static ZEND_COLD void undef_result_after_exception(void) { const zend_op *opline = EG(opline_before_exception); @@ -306,6 +307,18 @@ static zend_execute_data* ZEND_FASTCALL zend_jit_int_extend_stack_helper(uint32_ return call; } +#ifdef __SANITIZE_ADDRESS__ +static void ZEND_FASTCALL zend_jit_poison_memory_region_helper(void *addr, size_t size) +{ + ZEND_POISON_MEMORY_REGION(addr, size); +} + +static void ZEND_FASTCALL zend_jit_unpoison_memory_region_helper(void *addr, size_t size) +{ + ZEND_UNPOISON_MEMORY_REGION(addr, size); +} +#endif + static zval* ZEND_FASTCALL zend_jit_symtable_find(HashTable *ht, zend_string *str) { zend_ulong idx; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 2a2c6c79f17f9..8505ea2d23ae2 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3117,6 +3117,10 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_uninit_static_prop); REGISTER_HELPER(zend_jit_rope_end); REGISTER_HELPER(zend_fcall_interrupt); +# ifdef __SANITIZE_ADDRESS__ + REGISTER_HELPER(zend_jit_poison_memory_region_helper); + REGISTER_HELPER(zend_jit_unpoison_memory_region_helper); +# endif #ifndef ZTS REGISTER_DATA(EG(current_execute_data)); @@ -8485,6 +8489,11 @@ static int zend_jit_push_call_frame(zend_jit_ctx *jit, const zend_op *opline, co #endif ir_STORE(ref, ir_ADD_A(top, used_stack_ref)); +#ifdef __SANITIZE_ADDRESS__ + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_unpoison_memory_region_helper), + rx, used_stack_ref); +#endif + // JIT: zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object); if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || opline->opcode != ZEND_INIT_METHOD_CALL) { // JIT: ZEND_SET_CALL_INFO(call, 0, call_info); @@ -11046,6 +11055,11 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, may_throw = 1; } +#ifdef __SANITIZE_ADDRESS__ + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_poison_memory_region_helper), + jit_FP(jit), ir_SUB_A(ir_LOAD_A(jit_EG(vm_stack_top)), jit_FP(jit))); +#endif + // JIT: EG(vm_stack_top) = (zval*)execute_data ir_STORE(jit_EG(vm_stack_top), jit_FP(jit)); diff --git a/ext/zend_test/fiber.c b/ext/zend_test/fiber.c index 199d1b28b8cd6..38f75b24c523c 100644 --- a/ext/zend_test/fiber.c +++ b/ext/zend_test/fiber.c @@ -89,6 +89,7 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran EG(vm_stack_page_size) = ZEND_FIBER_VM_STACK_SIZE; execute_data = (zend_execute_data *) stack->top; + ZEND_UNPOISON_MEMORY_REGION(execute_data, sizeof(zend_execute_data)); memset(execute_data, 0, sizeof(zend_execute_data)); execute_data->func = (zend_function *) &zend_pass_function;