From 7fd37190e8fb14eb61a8da97ff0d6ca3dad1a9fa Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Mon, 7 Nov 2022 18:26:33 +0800 Subject: [PATCH] Add control for the native stack check with hardware trap (#1682) Add a new options to control the native stack hw bound check feature: - Besides the original option `cmake -DWAMR_DISABLE_HW_BOUND_CHECK=1/0`, add a new option `cmake -DWAMR_DISABLE_STACK_HW_BOUND_CHECK=1/0` - When the linear memory hw bound check is disabled, the stack hw bound check will be disabled automatically, no matter what the input option is - When the linear memory hw bound check is enabled, the stack hw bound check is enabled/disabled according to the value of input option - Besides the original option `--bounds-checks=1/0`, add a new option `--stack-bounds-checks=1/0` for wamrc Refer to: https://github.com/bytecodealliance/wasm-micro-runtime/issues/1677 --- build-scripts/SConscript_config | 1 + build-scripts/config_common.cmake | 7 +++++ core/config.h | 6 +++++ core/iwasm/common/wasm_runtime_common.c | 8 ++++++ core/iwasm/compilation/aot_emit_function.c | 4 +-- core/iwasm/compilation/aot_llvm.c | 26 +++++++++++++++++++ core/iwasm/compilation/aot_llvm.h | 4 +++ core/iwasm/include/aot_export.h | 1 + core/iwasm/interpreter/wasm_interp_classic.c | 3 ++- core/iwasm/interpreter/wasm_interp_fast.c | 3 ++- .../platform/common/posix/posix_thread.c | 21 ++++++++++++++- core/shared/platform/windows/win_thread.c | 6 +++++ doc/build_wamr.md | 8 ++++-- product-mini/platforms/nuttx/wamr.mk | 2 ++ wamr-compiler/main.c | 10 +++++++ 15 files changed, 103 insertions(+), 7 deletions(-) diff --git a/build-scripts/SConscript_config b/build-scripts/SConscript_config index df96940e..2401f3aa 100644 --- a/build-scripts/SConscript_config +++ b/build-scripts/SConscript_config @@ -92,6 +92,7 @@ else: if GetDepend(['WAMR_DISABLE_HW_BOUND_CHECK']): CPPDEFINES += ['WASM_DISABLE_HW_BOUND_CHECK=1'] + CPPDEFINES += ['WASM_DISABLE_STACK_HW_BOUND_CHECK=1'] print("[WAMR] Hardware boundary check disabled") if GetDepend(['WAMR_BUILD_SIMD']): diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 27696269..85f82e3b 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -205,9 +205,16 @@ else () endif () if (WAMR_DISABLE_HW_BOUND_CHECK EQUAL 1) add_definitions (-DWASM_DISABLE_HW_BOUND_CHECK=1) + add_definitions (-DWASM_DISABLE_STACK_HW_BOUND_CHECK=1) message (" Hardware boundary check disabled") else () add_definitions (-DWASM_DISABLE_HW_BOUND_CHECK=0) + if (WAMR_DISABLE_STACK_HW_BOUND_CHECK EQUAL 1) + add_definitions (-DWASM_DISABLE_STACK_HW_BOUND_CHECK=1) + message (" Hardware boundary check for native stack disabled") + else () + add_definitions (-DWASM_DISABLE_STACK_HW_BOUND_CHECK=0) + endif () endif () if (WAMR_BUILD_SIMD EQUAL 1) if (NOT WAMR_BUILD_TARGET MATCHES "RISCV64.*") diff --git a/core/config.h b/core/config.h index 96c80909..0ba617ce 100644 --- a/core/config.h +++ b/core/config.h @@ -260,6 +260,12 @@ #define WASM_DISABLE_HW_BOUND_CHECK 0 #endif +/* Disable native stack access boundary check with hardware + * trap or not, enable it by default if it is supported */ +#ifndef WASM_DISABLE_STACK_HW_BOUND_CHECK +#define WASM_DISABLE_STACK_HW_BOUND_CHECK 0 +#endif + /* Disable SIMD unless it is manualy enabled somewhere */ #ifndef WASM_ENABLE_SIMD #define WASM_ENABLE_SIMD 0 diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 220aa311..10440dac 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -143,9 +143,11 @@ runtime_signal_handler(void *sig_addr) WASMJmpBuf *jmpbuf_node; uint8 *mapped_mem_start_addr = NULL; uint8 *mapped_mem_end_addr = NULL; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 uint8 *stack_min_addr; uint32 page_size; uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; +#endif /* Check whether current thread is running wasm function */ if (exec_env_tls && exec_env_tls->handle == os_self_thread() @@ -159,9 +161,11 @@ runtime_signal_handler(void *sig_addr) mapped_mem_end_addr = memory_inst->memory_data + 8 * (uint64)BH_GB; } +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 /* Get stack info of current thread */ page_size = os_getpagesize(); stack_min_addr = os_thread_get_stack_boundary(); +#endif if (memory_inst && (mapped_mem_start_addr <= (uint8 *)sig_addr @@ -171,6 +175,7 @@ runtime_signal_handler(void *sig_addr) wasm_set_exception(module_inst, "out of bounds memory access"); os_longjmp(jmpbuf_node->jmpbuf, 1); } +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 else if (stack_min_addr - page_size <= (uint8 *)sig_addr && (uint8 *)sig_addr < stack_min_addr + page_size * guard_page_count) { @@ -179,6 +184,7 @@ runtime_signal_handler(void *sig_addr) wasm_set_exception(module_inst, "native stack overflow"); os_longjmp(jmpbuf_node->jmpbuf, 1); } +#endif } } #else @@ -230,6 +236,7 @@ runtime_exception_handler(EXCEPTION_POINTERS *exce_info) } } } +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 else if (ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) { /* Set stack overflow exception and let the wasm func continue to run, when the wasm func returns, the caller will check @@ -243,6 +250,7 @@ runtime_exception_handler(EXCEPTION_POINTERS *exce_info) return EXCEPTION_CONTINUE_EXECUTION; } } +#endif } os_printf("Unhandled exception thrown: exception code: 0x%lx, " diff --git a/core/iwasm/compilation/aot_emit_function.c b/core/iwasm/compilation/aot_emit_function.c index ae5420dc..cbcdebff 100644 --- a/core/iwasm/compilation/aot_emit_function.c +++ b/core/iwasm/compilation/aot_emit_function.c @@ -797,7 +797,7 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, callee_cell_num = aot_func->param_cell_num + aot_func->local_cell_num + 1; - if (comp_ctx->enable_bound_check + if (comp_ctx->enable_stack_bound_check && !check_stack_boundary(comp_ctx, func_ctx, callee_cell_num)) goto fail; @@ -1411,7 +1411,7 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Translate call non-import block */ LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_non_import); - if (comp_ctx->enable_bound_check + if (comp_ctx->enable_stack_bound_check && !check_stack_boundary(comp_ctx, func_ctx, param_cell_num + ext_cell_num + 1 diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index fa4aa681..f08cb754 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -1555,8 +1555,22 @@ aot_create_comp_context(AOTCompData *comp_data, aot_comp_option_t option) #ifndef OS_ENABLE_HW_BOUND_CHECK comp_ctx->enable_bound_check = true; + /* Always enable stack boundary check if `bounds-checks` + is enabled */ + comp_ctx->enable_stack_bound_check = true; #else comp_ctx->enable_bound_check = false; + /* When `bounds-checks` is disabled, we set stack boundary + check status according to the compilation option */ +#if WASM_DISABLE_STACK_HW_BOUND_CHECK != 0 + /* Native stack overflow check with hardware trap is disabled, + we need to enable the check by LLVM JITed/AOTed code */ + comp_ctx->enable_stack_bound_check = true; +#else + /* Native stack overflow check with hardware trap is enabled, + no need to enable the check by LLVM JITed/AOTed code */ + comp_ctx->enable_stack_bound_check = false; +#endif #endif } else { @@ -1868,6 +1882,18 @@ aot_create_comp_context(AOTCompData *comp_data, aot_comp_option_t option) } } + if (comp_ctx->enable_bound_check) { + /* Always enable stack boundary check if `bounds-checks` + is enabled */ + comp_ctx->enable_stack_bound_check = true; + } + else { + /* When `bounds-checks` is disabled, we set stack boundary + check status according to the input option */ + comp_ctx->enable_stack_bound_check = + (option->stack_bounds_checks == 1) ? true : false; + } + os_printf("Create AoT compiler with:\n"); os_printf(" target: %s\n", comp_ctx->target_arch); os_printf(" target cpu: %s\n", cpu); diff --git a/core/iwasm/compilation/aot_llvm.h b/core/iwasm/compilation/aot_llvm.h index 5cb84d58..219f36e7 100644 --- a/core/iwasm/compilation/aot_llvm.h +++ b/core/iwasm/compilation/aot_llvm.h @@ -310,6 +310,9 @@ typedef struct AOTCompContext { /* Bounday Check */ bool enable_bound_check; + /* Native stack bounday Check */ + bool enable_stack_bound_check; + /* 128-bit SIMD */ bool enable_simd; @@ -404,6 +407,7 @@ typedef struct AOTCompOption { uint32 size_level; uint32 output_format; uint32 bounds_checks; + uint32 stack_bounds_checks; char **custom_sections; uint32 custom_sections_count; } AOTCompOption, *aot_comp_option_t; diff --git a/core/iwasm/include/aot_export.h b/core/iwasm/include/aot_export.h index 9dc1441f..f8df168e 100644 --- a/core/iwasm/include/aot_export.h +++ b/core/iwasm/include/aot_export.h @@ -59,6 +59,7 @@ typedef struct AOTCompOption { uint32_t size_level; uint32_t output_format; uint32_t bounds_checks; + uint32_t stack_bounds_checks; char **custom_sections; uint32_t custom_sections_count; } AOTCompOption, *aot_comp_option_t; diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 30c40baf..446337e7 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -4115,7 +4115,8 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } argc = function->param_cell_num; -#ifndef OS_ENABLE_HW_BOUND_CHECK +#if !(defined(OS_ENABLE_HW_BOUND_CHECK) \ + && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0) if ((uint8 *)&prev_frame < exec_env->native_stack_boundary) { wasm_set_exception((WASMModuleInstance *)exec_env->module_inst, "native stack overflow"); diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index d2440d18..b1d9d3ff 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -3911,7 +3911,8 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } argc = function->param_cell_num; -#ifndef OS_ENABLE_HW_BOUND_CHECK +#if !(defined(OS_ENABLE_HW_BOUND_CHECK) \ + && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0) if ((uint8 *)&prev_frame < exec_env->native_stack_boundary) { wasm_set_exception((WASMModuleInstance *)exec_env->module_inst, "native stack overflow"); diff --git a/core/shared/platform/common/posix/posix_thread.c b/core/shared/platform/common/posix/posix_thread.c index 44b4135b..58370c20 100644 --- a/core/shared/platform/common/posix/posix_thread.c +++ b/core/shared/platform/common/posix/posix_thread.c @@ -425,6 +425,7 @@ os_thread_get_stack_boundary() */ static os_thread_local_attribute bool thread_signal_inited = false; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 /* The signal alternate stack base addr */ static os_thread_local_attribute uint8 *sigalt_stack_base_addr; @@ -488,6 +489,7 @@ destroy_stack_guard_pages() os_mprotect(stack_min_addr, page_size * guard_page_count, MMAP_PROT_READ | MMAP_PROT_WRITE); } +#endif /* end of WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 */ static void mask_signals(int how) @@ -553,13 +555,16 @@ int os_thread_signal_init(os_signal_handler handler) { struct sigaction sig_act; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 stack_t sigalt_stack_info; uint32 map_size = SIG_ALT_STACK_SIZE; uint8 *map_addr; +#endif if (thread_signal_inited) return 0; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 if (!init_stack_guard_pages()) { os_printf("Failed to init stack guard pages\n"); return -1; @@ -581,13 +586,17 @@ os_thread_signal_init(os_signal_handler handler) os_printf("Failed to init signal alternate stack\n"); goto fail2; } +#endif memset(&prev_sig_act_SIGSEGV, 0, sizeof(struct sigaction)); memset(&prev_sig_act_SIGBUS, 0, sizeof(struct sigaction)); /* Install signal hanlder */ sig_act.sa_sigaction = signal_callback; - sig_act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER; + sig_act.sa_flags = SA_SIGINFO | SA_NODEFER; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + sig_act.sa_flags |= SA_ONSTACK; +#endif sigemptyset(&sig_act.sa_mask); if (sigaction(SIGSEGV, &sig_act, &prev_sig_act_SIGSEGV) != 0 || sigaction(SIGBUS, &sig_act, &prev_sig_act_SIGBUS) != 0) { @@ -595,12 +604,15 @@ os_thread_signal_init(os_signal_handler handler) goto fail3; } +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 sigalt_stack_base_addr = map_addr; +#endif signal_handler = handler; thread_signal_inited = true; return 0; fail3: +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 memset(&sigalt_stack_info, 0, sizeof(stack_t)); sigalt_stack_info.ss_flags = SS_DISABLE; sigalt_stack_info.ss_size = map_size; @@ -609,17 +621,21 @@ fail2: os_munmap(map_addr, map_size); fail1: destroy_stack_guard_pages(); +#endif return -1; } void os_thread_signal_destroy() { +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 stack_t sigalt_stack_info; +#endif if (!thread_signal_inited) return; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 /* Disable signal alternate stack */ memset(&sigalt_stack_info, 0, sizeof(stack_t)); sigalt_stack_info.ss_flags = SS_DISABLE; @@ -629,6 +645,7 @@ os_thread_signal_destroy() os_munmap(sigalt_stack_base_addr, SIG_ALT_STACK_SIZE); destroy_stack_guard_pages(); +#endif thread_signal_inited = false; } @@ -648,6 +665,7 @@ os_signal_unmask() void os_sigreturn() { +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 #if defined(__APPLE__) #define UC_RESET_ALT_STACK 0x80000000 extern int __sigreturn(void *, int); @@ -656,5 +674,6 @@ os_sigreturn() after exiting the signal handler. */ __sigreturn(NULL, UC_RESET_ALT_STACK); #endif +#endif } #endif /* end of OS_ENABLE_HW_BOUND_CHECK */ diff --git a/core/shared/platform/windows/win_thread.c b/core/shared/platform/windows/win_thread.c index 4c70e9f8..a67b15e6 100644 --- a/core/shared/platform/windows/win_thread.c +++ b/core/shared/platform/windows/win_thread.c @@ -706,13 +706,19 @@ static os_thread_local_attribute bool thread_signal_inited = false; int os_thread_signal_init() { +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 ULONG StackSizeInBytes = 16 * 1024; +#endif bool ret; if (thread_signal_inited) return 0; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 ret = SetThreadStackGuarantee(&StackSizeInBytes); +#else + ret = true; +#endif if (ret) thread_signal_inited = true; return ret ? 0 : -1; diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 6c2eac6f..18404e77 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -80,9 +80,13 @@ cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM - **WAMR_BUILD_LIB_PTHREAD_SEMAPHORE**=1/0, default to disable if not set > Note: This feature depends on `lib-pthread`, it will be enabled automatically if this feature is enabled. -#### **Disable boundary check with hardware trap in AOT or JIT mode** +#### **Disable boundary check with hardware trap** - **WAMR_DISABLE_HW_BOUND_CHECK**=1/0, default to enable if not set and supported by platform -> Note: by default only platform linux/darwin/android/vxworks 64-bit will enable boundary check with hardware trap in AOT or JIT mode, and the wamrc tool will generate AOT code without boundary check instructions in all 64-bit targets except SGX to improve performance. +> Note: by default only platform linux/darwin/android/windows/vxworks 64-bit will enable the boundary check with hardware trap feature, and the wamrc tool will generate AOT code without boundary check instructions in all 64-bit targets except SGX to improve performance. The boundary check includes linear memory access boundary and native stack access boundary, if `WAMR_DISABLE_STACK_HW_BOUND_CHECK` below isn't set. + +#### **Disable native stack boundary check with hardware trap** +- **WAMR_DISABLE_STACK_HW_BOUND_CHECK**=1/0, default to enable if not set and supported by platform, same as `WAMR_DISABLE_HW_BOUND_CHECK`. +> Note: When boundary check with hardware trap is disabled, or `WAMR_DISABLE_HW_BOUND_CHECK` is set to 1, the native stack boundary check with hardware trap will be disabled too, no matter what value is set to `WAMR_DISABLE_STACK_HW_BOUND_CHECK`. And when boundary check with hardware trap is enabled, the status of this feature is set according to the value of `WAMR_DISABLE_STACK_HW_BOUND_CHECK`. #### **Enable tail call feature** - **WAMR_BUILD_TAIL_CALL**=1/0, default to disable if not set diff --git a/product-mini/platforms/nuttx/wamr.mk b/product-mini/platforms/nuttx/wamr.mk index 9d89dcee..bda4d6de 100644 --- a/product-mini/platforms/nuttx/wamr.mk +++ b/product-mini/platforms/nuttx/wamr.mk @@ -268,8 +268,10 @@ endif ifeq ($(CONFIG_INTERPRETERS_WAMR_DISABLE_HW_BOUND_CHECK),y) CFLAGS += -DWASM_DISABLE_HW_BOUND_CHECK=1 +CFLAGS += -DWASM_DISABLE_STACK_HW_BOUND_CHECK=1 else CFLAGS += -DWASM_DISABLE_HW_BOUND_CHECK=0 +CFLAGS += -DWASM_DISABLE_STACK_HW_BOUND_CHECK=0 endif ifeq ($(CONFIG_INTERPRETERS_WAMR_CUSTOM_NAME_SECTIONS),y) diff --git a/wamr-compiler/main.c b/wamr-compiler/main.c index b77533ed..28986e86 100644 --- a/wamr-compiler/main.c +++ b/wamr-compiler/main.c @@ -37,6 +37,11 @@ print_help() printf(" by default it is disabled in all 64-bit platforms except SGX and\n"); printf(" in these platforms runtime does bounds checks with hardware trap,\n"); printf(" and by default it is enabled in all 32-bit platforms\n"); + printf(" --stack-bounds-checks=1/0 Enable or disable the bounds checks for native stack:\n"); + printf(" if the option isn't set, the status is same as `--bounds-check`,\n"); + printf(" if the option is set:\n"); + printf(" (1) it is always enabled when `--bounds-checks` is enabled,\n"); + printf(" (2) else it is enabled/disabled according to the option value\n"); printf(" --format= Specifies the format of the output file\n"); printf(" The format supported:\n"); printf(" aot (default) AoT file\n"); @@ -139,6 +144,8 @@ main(int argc, char *argv[]) option.output_format = AOT_FORMAT_FILE; /* default value, enable or disable depends on the platform */ option.bounds_checks = 2; + /* default value, enable or disable depends on the platform */ + option.stack_bounds_checks = 2; option.enable_simd = true; option.enable_aux_stack_check = true; option.enable_bulk_memory = true; @@ -193,6 +200,9 @@ main(int argc, char *argv[]) else if (!strncmp(argv[0], "--bounds-checks=", 16)) { option.bounds_checks = (atoi(argv[0] + 16) == 1) ? 1 : 0; } + else if (!strncmp(argv[0], "--stack-bounds-checks=", 22)) { + option.stack_bounds_checks = (atoi(argv[0] + 22) == 1) ? 1 : 0; + } else if (!strncmp(argv[0], "--format=", 9)) { if (argv[0][9] == '\0') PRINT_HELP_AND_EXIT();