Skip to content

Poison VM stack #19334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}
Expand Down
22 changes: 22 additions & 0 deletions Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdint.h>

Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}
Expand All @@ -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)) {
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_fibers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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));
Expand Down
52 changes: 52 additions & 0 deletions Zend/zend_sanitizers.h
Original file line number Diff line number Diff line change
@@ -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 <sanitizer/asan_interface.h>
#else
# define ASAN_POISON_MEMORY_REGION(_ptr, _size)
# define ASAN_UNPOISON_MEMORY_REGION(_ptr, _size)
#endif

#if __has_feature(memory_sanitizer)
# include <sanitizer/msan_interface.h>
# 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 */
12 changes: 5 additions & 7 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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);
Expand Down
21 changes: 9 additions & 12 deletions Zend/zend_vm_execute.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions ext/opcache/jit/zend_jit_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions ext/opcache/jit/zend_jit_ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));

Expand Down
1 change: 1 addition & 0 deletions ext/zend_test/fiber.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down