Skip to content

Commit

Permalink
Add wasm_runtime_detect_native_stack_overflow_size (bytecodealliance#…
Browse files Browse the repository at this point in the history
…3355)

- Add a few API (bytecodealliance#3325)
   ```c
   wasm_runtime_detect_native_stack_overflow_size
   wasm_runtime_detect_native_stack_overflow
   ```
- Adapt the runtime to use them
- Adapt samples/native-stack-overflow to use them
- Add a few missing overflow checks in the interpreters
- Build and run the sample on the CI
  • Loading branch information
yamt authored and victoryang00 committed May 1, 2024
1 parent af6b1cb commit 8197b43
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 53 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/compilation_on_android_ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,13 @@ jobs:
./iwasm wasm-apps/trap.aot | grep "#" > call_stack_aot.txt
bash -x ../symbolicate.sh
- name: Build Sample [native-stack-overflow]
run: |
cd samples/native-stack-overflow
./build.sh
./run.sh test1
./run.sh test2
test:
needs:
[
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/compilation_on_macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,12 @@ jobs:
./iwasm wasm-apps/trap.wasm | grep "#" > call_stack.txt
./iwasm wasm-apps/trap.aot | grep "#" > call_stack_aot.txt
bash -x ../symbolicate.sh
# skip on arm64 (macos-14) for now
- name: Build Sample [native-stack-overflow]
if: matrix.os != 'macos-14'
run: |
cd samples/native-stack-overflow
./build.sh
./run.sh test1
./run.sh test2
8 changes: 8 additions & 0 deletions .github/workflows/nightly_run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,13 @@ jobs:
./build.sh
./run.sh
- name: Build Sample [native-stack-overflow]
run: |
cd samples/native-stack-overflow
./build.sh
./run.sh test1
./run.sh test2
- name: Build Sample [native-lib]
run: |
mkdir build && cd build
Expand All @@ -567,6 +574,7 @@ jobs:
python3 ./sample_test_run.py $(pwd)/out
exit $?
working-directory: ./wamr-app-framework/samples/simple

test:
needs:
[
Expand Down
11 changes: 2 additions & 9 deletions core/iwasm/aot/aot_runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1990,8 +1990,6 @@ invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr,
AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst;
WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls();
WASMJmpBuf jmpbuf_node = { 0 }, *jmpbuf_node_pop;
uint32 page_size = os_getpagesize();
uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT;
#ifdef BH_PLATFORM_WINDOWS
int result;
bool has_exception;
Expand All @@ -2002,10 +2000,7 @@ invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr,
/* Check native stack overflow firstly to ensure we have enough
native stack to run the following codes before actually calling
the aot function in invokeNative function. */
RECORD_STACK_USAGE(exec_env, (uint8 *)&module_inst);
if ((uint8 *)&module_inst
< exec_env->native_stack_boundary + page_size * guard_page_count) {
aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW);
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
return false;
}

Expand Down Expand Up @@ -2814,9 +2809,7 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
exec_env->native_stack_boundary must have been set, we don't set
it again */

RECORD_STACK_USAGE(exec_env, (uint8 *)&module_inst);
if ((uint8 *)&module_inst < exec_env->native_stack_boundary) {
aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW);
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
goto fail;
}

Expand Down
56 changes: 56 additions & 0 deletions core/iwasm/common/wasm_runtime_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -7047,3 +7047,59 @@ wasm_runtime_get_module_name(wasm_module_t module)

return "";
}

/*
* wasm_runtime_detect_native_stack_overflow
*
* - raise "native stack overflow" exception if available native stack
* at this point is less than WASM_STACK_GUARD_SIZE. in that case,
* return false.
*
* - update native_stack_top_min.
*/
bool
wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env)
{
uint8 *boundary = exec_env->native_stack_boundary;
RECORD_STACK_USAGE(exec_env, (uint8 *)&boundary);
if (boundary == NULL) {
/* the platfrom doesn't support os_thread_get_stack_boundary */
return true;
}
#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0
uint32 page_size = os_getpagesize();
uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT;
boundary = boundary + page_size * guard_page_count;
#endif
if ((uint8 *)&boundary < boundary) {
wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env),
"native stack overflow");
return false;
}
return true;
}

bool
wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env,
uint32 requested_size)
{
uint8 *boundary = exec_env->native_stack_boundary;
RECORD_STACK_USAGE(exec_env, (uint8 *)&boundary);
if (boundary == NULL) {
/* the platfrom doesn't support os_thread_get_stack_boundary */
return true;
}
#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0
uint32 page_size = os_getpagesize();
uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT;
boundary = boundary + page_size * guard_page_count;
#endif
/* adjust the boundary for the requested size */
boundary = boundary - WASM_STACK_GUARD_SIZE + requested_size;
if ((uint8 *)&boundary < boundary) {
wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env),
"native stack overflow");
return false;
}
return true;
}
51 changes: 51 additions & 0 deletions core/iwasm/include/wasm_export.h
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,57 @@ wasm_runtime_set_module_name(wasm_module_t module, const char *name,
WASM_RUNTIME_API_EXTERN const char *
wasm_runtime_get_module_name(wasm_module_t module);

/*
* wasm_runtime_detect_native_stack_overflow
*
* Detect native stack shortage.
* Ensure that the calling thread still has a reasonable amount of
* native stack (WASM_STACK_GUARD_SIZE bytes) available.
*
* If enough stack is left, this function returns true.
* Otherwise, this function raises a "native stack overflow" trap and
* returns false.
*
* Note: please do not expect a very strict detection. it's a good idea
* to give some margins. wasm_runtime_detect_native_stack_overflow itself
* requires a small amount of stack to run.
*/
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_detect_native_stack_overflow(wasm_exec_env_t exec_env);

/*
* wasm_runtime_detect_native_stack_overflow_size
*
* Similar to wasm_runtime_detect_native_stack_overflow,
* but use the caller-specified size instead of WASM_STACK_GUARD_SIZE.
*
* An expected usage:
* ```c
* __attribute__((noinline)) // inlining can break the stack check
* void stack_hog(void)
* {
* // consume a lot of stack here
* }
*
* void
* stack_hog_wrapper(exec_env) {
* // the amount of stack stack_hog would consume,
* // plus a small margin
* uint32_t size = 10000000;
*
* if (!wasm_runtime_detect_native_stack_overflow_size(exec_env, size)) {
* // wasm_runtime_detect_native_stack_overflow_size has raised
* // a trap.
* return;
* }
* stack_hog();
* }
* ```
*/
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_detect_native_stack_overflow_size(wasm_exec_env_t exec_env,
uint32_t required_size);

#ifdef __cplusplus
}
#endif
Expand Down
25 changes: 19 additions & 6 deletions core/iwasm/interpreter/wasm_interp_classic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,10 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst,
uint8 *frame_ref;
#endif

if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
return;
}

all_cell_num = local_cell_num;
#if WASM_ENABLE_GC != 0
all_cell_num += (local_cell_num + 3) / 4;
Expand Down Expand Up @@ -1295,6 +1299,14 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst,
uintptr_t aux_stack_origin_boundary = 0;
uintptr_t aux_stack_origin_bottom = 0;

/*
* perform stack overflow check before calling
* wasm_interp_call_func_bytecode recursively.
*/
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
return;
}

if (!sub_func_inst) {
snprintf(buf, sizeof(buf),
"failed to call unlinked import function (%s, %s)",
Expand Down Expand Up @@ -7135,12 +7147,13 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
}
argc = function->param_cell_num;

RECORD_STACK_USAGE(exec_env, (uint8 *)&prev_frame);
#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");
#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0
/*
* wasm_runtime_detect_native_stack_overflow is done by
* call_wasm_with_hw_bound_check.
*/
#else
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
return;
}
#endif
Expand Down
25 changes: 19 additions & 6 deletions core/iwasm/interpreter/wasm_interp_fast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,10 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst,
all_cell_num += (local_cell_num + 3) / 4;
#endif

if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
return;
}

if (!(frame =
ALLOC_FRAME(exec_env, wasm_interp_interp_frame_size(all_cell_num),
prev_frame)))
Expand Down Expand Up @@ -1275,6 +1279,14 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst,
uintptr_t aux_stack_origin_boundary = 0;
uintptr_t aux_stack_origin_bottom = 0;

/*
* perform stack overflow check before calling
* wasm_interp_call_func_bytecode recursively.
*/
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
return;
}

if (!sub_func_inst) {
snprintf(buf, sizeof(buf),
"failed to call unlinked import function (%s, %s)",
Expand Down Expand Up @@ -6081,12 +6093,13 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
}
argc = function->param_cell_num;

RECORD_STACK_USAGE(exec_env, (uint8 *)&prev_frame);
#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");
#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0
/*
* wasm_runtime_detect_native_stack_overflow is done by
* call_wasm_with_hw_bound_check.
*/
#else
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
return;
}
#endif
Expand Down
7 changes: 1 addition & 6 deletions core/iwasm/interpreter/wasm_runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -3139,8 +3139,6 @@ call_wasm_with_hw_bound_check(WASMModuleInstance *module_inst,
{
WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls();
WASMJmpBuf jmpbuf_node = { 0 }, *jmpbuf_node_pop;
uint32 page_size = os_getpagesize();
uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT;
WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env);
uint8 *prev_top = exec_env->wasm_stack.top;
#ifdef BH_PLATFORM_WINDOWS
Expand All @@ -3153,10 +3151,7 @@ call_wasm_with_hw_bound_check(WASMModuleInstance *module_inst,
/* Check native stack overflow firstly to ensure we have enough
native stack to run the following codes before actually calling
the aot function in invokeNative function. */
RECORD_STACK_USAGE(exec_env, (uint8 *)&exec_env_tls);
if ((uint8 *)&exec_env_tls
< exec_env->native_stack_boundary + page_size * guard_page_count) {
wasm_set_exception(module_inst, "native stack overflow");
if (!wasm_runtime_detect_native_stack_overflow(exec_env)) {
return;
}

Expand Down
5 changes: 5 additions & 0 deletions samples/native-stack-overflow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ if (CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GRE
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-usage")
endif ()

# GCC doesn't have disable_tail_calls attribute
if (CMAKE_C_COMPILER_ID MATCHES "GNU")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-optimize-sibling-calls")
endif ()

# build out vmlib
set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
Expand Down
4 changes: 2 additions & 2 deletions samples/native-stack-overflow/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ echo "#################### build wasm apps done"

echo "#################### aot-compile"
WAMRC=${WAMR_DIR}/wamr-compiler/build/wamrc
${WAMRC} -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot ${OUT_DIR}/wasm-apps/${OUT_FILE}
${WAMRC} -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot --size-level=0 ${OUT_DIR}/wasm-apps/${OUT_FILE}

echo "#################### aot-compile (--bounds-checks=1)"
${WAMRC} -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot.bounds-checks --bounds-checks=1 ${OUT_DIR}/wasm-apps/${OUT_FILE}
${WAMRC} -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot.bounds-checks --size-level=0 --bounds-checks=1 ${OUT_DIR}/wasm-apps/${OUT_FILE}
2 changes: 1 addition & 1 deletion samples/native-stack-overflow/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ main(int argc, char **argv)
"--------\n");

unsigned int stack;
unsigned int prevstack;
unsigned int prevstack = 0; /* appease GCC -Wmaybe-uninitialized */
unsigned int stack_range_start = 0;
unsigned int stack_range_end = 4096 * 6;
unsigned int step = 16;
Expand Down
Loading

0 comments on commit 8197b43

Please sign in to comment.