diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 5f7746f6a..1a77d4cac 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -671,6 +671,10 @@ if (WAMR_BUILD_AOT_VALIDATOR EQUAL 1) message (" AOT validator enabled") add_definitions (-DWASM_ENABLE_AOT_VALIDATOR=1) endif () +if (WAMR_BUILD_INSTRUCTION_METERING EQUAL 1) + message (" Instruction metering enabled") + add_definitions (-DWASM_ENABLE_INSTRUCTION_METERING=1) +endif () ######################################## # Show Phase4 Wasm proposals status. diff --git a/core/config.h b/core/config.h index cb1189c96..a4e1499e3 100644 --- a/core/config.h +++ b/core/config.h @@ -716,4 +716,8 @@ unless used elsewhere */ #define WASM_ENABLE_AOT_VALIDATOR 0 #endif +#ifndef WASM_ENABLE_INSTRUCTION_METERING +#define WASM_ENABLE_INSTRUCTION_METERING 0 +#endif + #endif /* end of _CONFIG_H_ */ diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c index 27484dfc5..47752950f 100644 --- a/core/iwasm/common/wasm_exec_env.c +++ b/core/iwasm/common/wasm_exec_env.c @@ -85,6 +85,10 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, wasm_runtime_dump_exec_env_mem_consumption(exec_env); #endif +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + exec_env->instructions_to_execute = -1; +#endif + return exec_env; #ifdef OS_ENABLE_HW_BOUND_CHECK diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h index ce0c1fa7f..5d80312fb 100644 --- a/core/iwasm/common/wasm_exec_env.h +++ b/core/iwasm/common/wasm_exec_env.h @@ -87,6 +87,11 @@ typedef struct WASMExecEnv { uint8 *bottom; } wasm_stack; +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + /* instructions to execute */ + int instructions_to_execute; +#endif + #if WASM_ENABLE_FAST_JIT != 0 /** * Cache for diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index d33c02727..b316a6e30 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -2285,6 +2285,15 @@ wasm_runtime_access_exce_check_guard_page() } #endif +#if WASM_ENABLE_INSTRUCTION_METERING != 0 +void +wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, + int instructions_to_execute) +{ + exec_env->instructions_to_execute = instructions_to_execute; +} +#endif + WASMFuncType * wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, uint32 module_type) diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index 8ac032bf8..64a6cd793 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -791,9 +791,17 @@ WASM_RUNTIME_API_EXTERN void wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env, uint8 *native_stack_boundary); -#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 +#if WASM_ENABLE_INSTRUCTION_METERING != 0 /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, + int instructions_to_execute); +#endif + +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN +void wasm_runtime_set_bounds_checks(WASMModuleInstanceCommon *module_inst, bool enable); diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index b73a0364e..b4ab34bea 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -1821,6 +1821,20 @@ WASM_RUNTIME_API_EXTERN void wasm_runtime_set_native_stack_boundary(wasm_exec_env_t exec_env, uint8_t *native_stack_boundary); +/** + * Set the instruction count limit to the execution environment. + * By default the instruction count limit is -1, which means no limit. + * However, if the instruction count limit is set to a positive value, + * the execution will be terminated when the instruction count reaches + * the limit. + * + * @param exec_env the execution environment + * @param instruction_count the instruction count limit + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env, + int instruction_count); + /** * Dump runtime memory consumption, including: * Exec env memory consumption diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 41ac4c724..1e98b0fa9 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -1516,10 +1516,13 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, } \ os_mutex_unlock(&exec_env->wait_lock); \ } \ + CHECK_INSTRUCTION_LIMIT(); \ goto *handle_table[*frame_ip++]; \ } while (0) #else -#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH() +#define HANDLE_OP_END() \ + CHECK_INSTRUCTION_LIMIT(); \ + FETCH_OPCODE_AND_DISPATCH() #endif #else /* else of WASM_ENABLE_LABELS_AS_VALUES */ @@ -1542,9 +1545,12 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, } \ os_mutex_unlock(&exec_env->wait_lock); \ } \ + CHECK_INSTRUCTION_LIMIT(); \ continue; #else -#define HANDLE_OP_END() continue +#define HANDLE_OP_END() \ + CHECK_INSTRUCTION_LIMIT(); \ + continue; #endif #endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ @@ -1562,6 +1568,18 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global) #endif } +#if WASM_ENABLE_INSTRUCTION_METERING != 0 +#define CHECK_INSTRUCTION_LIMIT() \ + if (instructions_left == 0) { \ + wasm_set_exception(module, "instruction limit exceeded"); \ + goto got_exception; \ + } \ + else if (instructions_left > 0) \ + instructions_left--; +#else +#define CHECK_INSTRUCTION_LIMIT() (void)0 +#endif + static void wasm_interp_call_func_bytecode(WASMModuleInstance *module, WASMExecEnv *exec_env, @@ -1605,6 +1623,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 local_idx, local_offset, global_idx; uint8 local_type, *global_addr; uint32 cache_index, type_index, param_cell_num, cell_num; + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + int instructions_left = -1; + if (exec_env) { + instructions_left = exec_env->instructions_to_execute; + } +#endif + #if WASM_ENABLE_EXCE_HANDLING != 0 int32_t exception_tag_index; #endif diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 681612380..4e5edf41b 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -105,6 +105,19 @@ typedef float64 CellType_F64; goto unaligned_atomic; \ } while (0) +#if WASM_ENABLE_INSTRUCTION_METERING != 0 +#define CHECK_INSTRUCTION_LIMIT() \ + if (instructions_left == 0) { \ + wasm_set_exception(module, "instruction limit exceeded"); \ + goto got_exception; \ + } \ + else if (instructions_left > 0) \ + instructions_left--; + +#else +#define CHECK_INSTRUCTION_LIMIT() (void)0 +#endif + static inline uint32 rotl32(uint32 n, uint32 c) { @@ -1441,6 +1454,7 @@ wasm_interp_dump_op_count() do { \ const void *p_label_addr = *(void **)frame_ip; \ frame_ip += sizeof(void *); \ + CHECK_INSTRUCTION_LIMIT(); \ goto *p_label_addr; \ } while (0) #else @@ -1452,6 +1466,7 @@ wasm_interp_dump_op_count() /* int32 relative offset was emitted in 64-bit target */ \ p_label_addr = label_base + (int32)LOAD_U32_WITH_2U16S(frame_ip); \ frame_ip += sizeof(int32); \ + CHECK_INSTRUCTION_LIMIT(); \ goto *p_label_addr; \ } while (0) #else @@ -1462,6 +1477,7 @@ wasm_interp_dump_op_count() /* uint32 label address was emitted in 32-bit target */ \ p_label_addr = (void *)(uintptr_t)LOAD_U32_WITH_2U16S(frame_ip); \ frame_ip += sizeof(int32); \ + CHECK_INSTRUCTION_LIMIT(); \ goto *p_label_addr; \ } while (0) #endif @@ -1538,6 +1554,13 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint8 *maddr = NULL; uint32 local_idx, local_offset, global_idx; uint8 opcode = 0, local_type, *global_addr; + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + int instructions_left = -1; + if (exec_env) { + instructions_left = exec_env->instructions_to_execute; + } +#endif #if !defined(OS_ENABLE_HW_BOUND_CHECK) \ || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 #if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 6425450bd..94dd96284 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -327,6 +327,10 @@ And the wasm app can calls below APIs to allocate/free memory from/to the shared - **WAMR_BUILD_SHRUNK_MEMORY**=1/0, default to enable if not set > Note: When enabled, this feature will reduce memory usage by decreasing the size of the linear memory, particularly when the `memory.grow` opcode is not used and memory usage is somewhat predictable. +## **Instruction metering** +- **WAMR_BUILD_INSTRUCTION_METERING**=1/0, default to disable if not set +> Note: Enabling this feature allows limiting the number of instructions a wasm module instance can execute. Use the `wasm_runtime_set_instruction_count_limit(...)` API before calling `wasm_runtime_call_*(...)` APIs to enforce this limit. + ## **Combination of configurations:** We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command: