Host function gas cost (#5488)

* Update Wamr to 2.3.1
* Add gas cost per host-function
* Fix windows build
* Fix wasm test
* Add no import test
This commit is contained in:
Olek
2025-06-12 15:54:49 -04:00
committed by GitHub
parent 6e8a5f0f4e
commit 51a9c0ff59
10 changed files with 516 additions and 403 deletions

View File

@@ -17,7 +17,7 @@ runs:
conan export external/rocksdb rocksdb/9.7.3@
conan export external/soci soci/4.0.3@
conan export external/nudb nudb/2.0.8@
conan export -k external/wamr wamr/2.2.0@
conan export external/wamr wamr/2.3.1@
- name: add Ripple Conan remote
shell: bash
run: |

View File

@@ -408,7 +408,7 @@ jobs:
conan profile update 'conf.tools.build:cxxflags+=["-DBOOST_ASIO_DISABLE_CONCEPTS"]' default
conan export external/snappy snappy/1.1.10@
conan export external/soci soci/4.0.3@
conan export -k external/wamr wamr/2.2.0@
conan export external/wamr wamr/2.3.1@
- name: build dependencies
run: |

View File

@@ -31,7 +31,7 @@ class Xrpl(ConanFile):
'openssl/1.1.1v',
'soci/4.0.3',
'zlib/1.3.1',
'wamr/2.2.0',
'wamr/2.3.1',
]
tool_requires = [

View File

@@ -1,5 +1,5 @@
patches:
2.2.0:
2.3.1:
- patch_description: add metering to iwasm interpreter
patch_file: patches/ripp_metering.patch
patch_type: conan

View File

@@ -13,7 +13,7 @@ required_conan_version = ">=1.55.0"
class WamrConan(ConanFile):
name = "wamr"
version = "2.2.0"
version = "2.3.1"
license = "Apache License v2.0"
url = "https://github.com/bytecodealliance/wasm-micro-runtime.git"
description = "Webassembly micro runtime"
@@ -42,7 +42,7 @@ class WamrConan(ConanFile):
git = tools.Git()
git.clone(
"https://github.com/bytecodealliance/wasm-micro-runtime.git",
"c883fafead005e87ad3122b05409886f507c1cb0",
"2a303861cc916dc182b7fecaa0aacc1b797e7ac6",
shallow=True,
)
# get(self, **self.conan_data["sources"][self.version], strip_root=True)
@@ -89,3 +89,4 @@ class WamrConan(ConanFile):
self.cpp_info.libs = ["iwasm"]
self.cpp_info.names["cmake_find_package"] = "wamr"
self.cpp_info.names["cmake_find_package_multi"] = "wamr"

View File

@@ -1,5 +1,5 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 551991f8..5f48a0b8 100644
index 88a1642b..aeb29912 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
@@ -11,97 +11,127 @@ index 551991f8..5f48a0b8 100644
option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake
index 1cb50235..bd103022 100644
--- a/build-scripts/config_common.cmake
+++ b/build-scripts/config_common.cmake
@@ -669,6 +669,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 cb1189c9..a4e1499e 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_c_api.c b/core/iwasm/common/wasm_c_api.c
index 269ec577..bc6fd01b 100644
index 269ec577..34eb7c34 100644
--- a/core/iwasm/common/wasm_c_api.c
+++ b/core/iwasm/common/wasm_c_api.c
@@ -5389,3 +5389,8 @@ wasm_instance_get_wasm_func_exec_time(const wasm_instance_t *instance,
@@ -3242,10 +3242,20 @@ wasm_func_copy(const wasm_func_t *func)
cloned->func_idx_rt = func->func_idx_rt;
cloned->inst_comm_rt = func->inst_comm_rt;
+ cloned->gas = func->gas;
RETURN_OBJ(cloned, wasm_func_delete)
}
+uint32_t
+wasm_func_set_gas(wasm_func_t *func, uint32_t gas)
+{
+ if(!func) return 0;
+
+ func->gas = gas;
+ return gas;
+}
+
own wasm_functype_t *
wasm_func_type(const wasm_func_t *func)
{
@@ -4998,11 +5008,11 @@ wasm_instance_new_with_args_ex(wasm_store_t *store, const wasm_module_t *module,
goto failed;
}
+ WASMModuleInstance *wasm_module_inst = NULL;
/* create the c-api func import list */
#if WASM_ENABLE_INTERP != 0
if (instance->inst_comm_rt->module_type == Wasm_Module_Bytecode) {
- WASMModuleInstance *wasm_module_inst =
- (WASMModuleInstance *)instance->inst_comm_rt;
+ wasm_module_inst = (WASMModuleInstance *)instance->inst_comm_rt;
p_func_imports = &(wasm_module_inst->c_api_func_imports);
import_func_count = MODULE_INTERP(module)->import_function_count;
}
@@ -5052,6 +5062,13 @@ wasm_instance_new_with_args_ex(wasm_store_t *store, const wasm_module_t *module,
}
bh_assert(func_import->func_ptr_linked);
+ // fill gas
+ if(wasm_module_inst) {
+ WASMFunctionInstance *fi = wasm_module_inst->e->functions + func_host->func_idx_rt;
+ if(fi) fi->gas = func_host->gas;
+ }
+
+
func_import++;
}
@@ -5389,3 +5406,8 @@ wasm_instance_get_wasm_func_exec_time(const wasm_instance_t *instance,
return -1.0;
#endif
}
+
+wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t*instance)
+wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t *instance)
+{
+ return wasm_runtime_get_exec_env_singleton(instance->inst_comm_rt);
+}
diff --git a/core/iwasm/common/wasm_c_api_internal.h b/core/iwasm/common/wasm_c_api_internal.h
index 49a17a96..19a85980 100644
--- a/core/iwasm/common/wasm_c_api_internal.h
+++ b/core/iwasm/common/wasm_c_api_internal.h
@@ -142,6 +142,10 @@ struct wasm_func_t {
void (*finalizer)(void *);
} cb_env;
} u;
+
+ // gas cost for import func
+ uint32 gas;
+
/*
* an index in both functions runtime instance lists
* of interpreter mode and aot mode
diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c
index e33fd9f3..d1ff9c41 100644
index 47752950..d5821c4f 100644
--- a/core/iwasm/common/wasm_exec_env.c
+++ b/core/iwasm/common/wasm_exec_env.c
@@ -85,6 +85,12 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
wasm_runtime_dump_exec_env_mem_consumption(exec_env);
@@ -86,7 +86,9 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
#endif
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+ exec_env->instructions_to_execute = -1;
#if WASM_ENABLE_INSTRUCTION_METERING != 0
- exec_env->instructions_to_execute = -1;
+ exec_env->instructions_to_execute = INT64_MAX;
+ for(int i = 0; i < 256; ++i)
+ exec_env->instructions_schedule[i] = 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 ce0c1fa7..2713a092 100644
--- a/core/iwasm/common/wasm_exec_env.h
+++ b/core/iwasm/common/wasm_exec_env.h
@@ -87,6 +87,12 @@ typedef struct WASMExecEnv {
uint8 *bottom;
} wasm_stack;
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+ /* instructions to execute */
+ int64 instructions_to_execute;
+ int64 instructions_schedule[256];
+#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 d33c0272..21bfdf29 100644
--- a/core/iwasm/common/wasm_runtime_common.c
+++ b/core/iwasm/common/wasm_runtime_common.c
@@ -2285,6 +2285,31 @@ wasm_runtime_access_exce_check_guard_page()
}
#endif
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+
+void
+wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
return exec_env;
diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h
index 5d80312f..2713a092 100644
--- a/core/iwasm/common/wasm_exec_env.h
+++ b/core/iwasm/common/wasm_exec_env.h
@@ -89,7 +89,8 @@ typedef struct WASMExecEnv {
#if WASM_ENABLE_INSTRUCTION_METERING != 0
/* instructions to execute */
- int instructions_to_execute;
+ int64 instructions_to_execute;
+ int64 instructions_schedule[256];
#endif
#if WASM_ENABLE_FAST_JIT != 0
diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c
index dcee0aea..2559be21 100644
--- a/core/iwasm/common/wasm_runtime_common.c
+++ b/core/iwasm/common/wasm_runtime_common.c
@@ -2288,10 +2288,26 @@ wasm_runtime_access_exce_check_guard_page()
#if WASM_ENABLE_INSTRUCTION_METERING != 0
void
wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
- int instructions_to_execute)
+ int64 instructions_to_execute)
+{
+ exec_env->instructions_to_execute = instructions_to_execute;
+}
{
+ if(instructions_to_execute == -1)
+ instructions_to_execute = INT64_MAX;
exec_env->instructions_to_execute = instructions_to_execute;
}
+
+int64
+wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env)
@@ -116,13 +146,10 @@ index d33c0272..21bfdf29 100644
+ for(int i = 0; i < 256; ++i)
+ exec_env->instructions_schedule[i] = instructions_schedule[i];
+}
+
+#endif
+
#endif
WASMFuncType *
wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function,
uint32 module_type)
@@ -7792,13 +7817,14 @@ wasm_runtime_get_module_name(wasm_module_t module)
@@ -7805,13 +7821,14 @@ wasm_runtime_get_module_name(wasm_module_t module)
bool
wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env)
{
@@ -138,7 +165,7 @@ index d33c0272..21bfdf29 100644
uint32 page_size = os_getpagesize();
uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT;
boundary = boundary + page_size * guard_page_count;
@@ -7808,6 +7834,7 @@ wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env)
@@ -7821,6 +7838,7 @@ wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env)
"native stack overflow");
return false;
}
@@ -146,7 +173,7 @@ index d33c0272..21bfdf29 100644
return true;
}
@@ -7830,7 +7857,7 @@ wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env,
@@ -7843,7 +7861,7 @@ wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env,
boundary = boundary - WASM_STACK_GUARD_SIZE + requested_size;
if ((uint8 *)&boundary < boundary) {
wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env),
@@ -156,19 +183,14 @@ index d33c0272..21bfdf29 100644
}
return true;
diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h
index 8ac032bf..5ca5d489 100644
index 64a6cd79..f4c55e2f 100644
--- a/core/iwasm/common/wasm_runtime_common.h
+++ b/core/iwasm/common/wasm_runtime_common.h
@@ -791,9 +791,25 @@ 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
+
@@ -795,7 +795,14 @@ wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env,
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN void
+wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
- int instructions_to_execute);
+ int64 instructions_to_execute);
+WASM_RUNTIME_API_EXTERN int64
+wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env);
@@ -177,17 +199,11 @@ index 8ac032bf..5ca5d489 100644
+wasm_runtime_set_instruction_schedule(WASMExecEnv *exec_env,
+ int64 const *instructions_schedule);
+
+#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);
#endif
#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h
index 241a0eec..9eb0dde1 100644
index 241a0eec..1141744c 100644
--- a/core/iwasm/include/wasm_c_api.h
+++ b/core/iwasm/include/wasm_c_api.h
@@ -19,8 +19,10 @@
@@ -202,7 +218,16 @@ index 241a0eec..9eb0dde1 100644
#endif
#else
#define WASM_API_EXTERN
@@ -701,6 +703,11 @@ WASM_API_EXTERN double wasm_instance_sum_wasm_exec_time(const wasm_instance_t*);
@@ -592,6 +594,8 @@ WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*);
WASM_API_EXTERN own wasm_trap_t* wasm_func_call(
const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results);
+WASM_API_EXTERN own uint32_t wasm_func_set_gas(wasm_func_t*, uint32_t);
+
// Global Instances
@@ -701,6 +705,11 @@ WASM_API_EXTERN double wasm_instance_sum_wasm_exec_time(const wasm_instance_t*);
// func_name. If the function is not found, return 0.
WASM_API_EXTERN double wasm_instance_get_wasm_func_exec_time(const wasm_instance_t*, const char *);
@@ -215,7 +240,7 @@ index 241a0eec..9eb0dde1 100644
// Convenience
diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h
index b73a0364..3fd0949f 100644
index b4ab34be..3fd0949f 100644
--- a/core/iwasm/include/wasm_export.h
+++ b/core/iwasm/include/wasm_export.h
@@ -20,8 +20,10 @@
@@ -230,22 +255,11 @@ index b73a0364..3fd0949f 100644
#endif
#elif defined(__GNUC__) || defined(__clang__)
#define WASM_RUNTIME_API_EXTERN __attribute__((visibility("default")))
@@ -1821,6 +1823,27 @@ 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,
@@ -1833,7 +1835,14 @@ wasm_runtime_set_native_stack_boundary(wasm_exec_env_t exec_env,
*/
WASM_RUNTIME_API_EXTERN void
wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env,
- int instruction_count);
+ int64_t instruction_count);
+
+WASM_RUNTIME_API_EXTERN int64_t
@@ -254,132 +268,122 @@ index b73a0364..3fd0949f 100644
+WASM_RUNTIME_API_EXTERN void
+wasm_runtime_set_instruction_schedule(wasm_exec_env_t exec_env,
+ int64_t const *instructions_schedule);
+
/**
* 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 41ac4c72..bd8f714a 100644
index 1e98b0fa..ae24ff8b 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,20 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
#endif
@@ -1569,13 +1569,14 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
}
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+#define CHECK_INSTRUCTION_LIMIT() \
+ if (instructions_to_execute >= 0) \
+ { \
+ instructions_to_execute -= instructions_schedule[opcode];\
+ if (instructions_to_execute < 0) { \
+ wasm_set_exception(module, "instruction limit exceeded"); \
+ goto got_exception; \
+ } \
+ }
+#else
+#define CHECK_INSTRUCTION_LIMIT() (void)0
+#endif
+
static void
wasm_interp_call_func_bytecode(WASMModuleInstance *module,
WASMExecEnv *exec_env,
@@ -1605,6 +1625,17 @@ 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
+ int64 instructions_to_execute = -1;
+ int64 const *instructions_schedule = NULL;
+ if(exec_env)
+ {
+ instructions_to_execute = exec_env->instructions_to_execute;
+ instructions_schedule = exec_env->instructions_schedule;
+ }
+#endif
+
#if WASM_ENABLE_EXCE_HANDLING != 0
int32_t exception_tag_index;
#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--;
+#define CHECK_INSTRUCTION_LIMIT() \
+ do { \
+ instructions_left -= instructions_schedule[opcode]; \
+ if (instructions_left < 0) { \
+ wasm_set_exception(module, "instruction limit exceeded"); \
+ goto got_exception; \
+ } \
+ } while (0)
#else
#define CHECK_INSTRUCTION_LIMIT() (void)0
#endif
@@ -6859,6 +6890,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
@@ -1625,9 +1626,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
uint32 cache_index, type_index, param_cell_num, cell_num;
#if WASM_ENABLE_INSTRUCTION_METERING != 0
- int instructions_left = -1;
+ int64 instructions_left = INT64_MAX;
+ int64 const *instructions_schedule = NULL;
if (exec_env) {
instructions_left = exec_env->instructions_to_execute;
+ instructions_schedule = exec_env->instructions_schedule;
}
#endif
@@ -6885,6 +6888,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
FREE_FRAME(exec_env, frame);
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+ if(exec_env)
+ exec_env->instructions_to_execute = instructions_to_execute;
+ exec_env->instructions_to_execute = instructions_left;
+#endif
+
if (!prev_frame->ip) {
/* Called from native. */
return;
@@ -6899,6 +6935,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
@@ -6925,6 +6933,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
}
#endif
SYNC_ALL_TO_FRAME();
+
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+ if(exec_env)
+ exec_env->instructions_to_execute = instructions_to_execute;
+ exec_env->instructions_to_execute = instructions_left;
+#endif
+
return;
#if WASM_ENABLE_LABELS_AS_VALUES == 0
diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c
index f33ad60e..9cbf2010 100644
index 4e5edf41..04c39e1f 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; \
@@ -106,14 +106,14 @@ typedef float64 CellType_F64;
} while (0)
+#if WASM_ENABLE_INSTRUCTION_METERING != 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--;
-
+#define CHECK_INSTRUCTION_LIMIT() \
+ if (instructions_to_execute >= 0) { \
+ instructions_to_execute -= instructions_schedule[opcode]; \
+ if (instructions_to_execute < 0) { \
+ do { \
+ instructions_left -= instructions_schedule[opcode]; \
+ if (instructions_left < 0) { \
+ wasm_set_exception(module, "instruction limit exceeded"); \
+ goto got_exception; \
+ } \
+ }
+#else
+#define CHECK_INSTRUCTION_LIMIT() (void)0
+#endif
+
static inline uint32
rotl32(uint32 n, uint32 c)
{
@@ -1466,12 +1479,14 @@ wasm_interp_dump_op_count()
+ } while (0)
#else
#define CHECK_INSTRUCTION_LIMIT() (void)0
#endif
@@ -1454,7 +1454,6 @@ 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
@@ -1466,7 +1465,6 @@ 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
@@ -1477,17 +1475,18 @@ 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
#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */
@@ -396,46 +400,91 @@ index f33ad60e..9cbf2010 100644
#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */
@@ -1538,6 +1553,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
uint8 *maddr = NULL;
uint32 local_idx, local_offset, global_idx;
@@ -1508,6 +1507,25 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
#endif
}
+static int64 const def_instructions_schedule[256] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
+};
+
static void
wasm_interp_call_func_bytecode(WASMModuleInstance *module,
WASMExecEnv *exec_env,
@@ -1556,9 +1574,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
uint8 opcode = 0, local_type, *global_addr;
+
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+ int64 instructions_to_execute = -1;
+ int64 const *instructions_schedule = NULL;
+ if (exec_env) {
+ instructions_to_execute = exec_env->instructions_to_execute;
#if WASM_ENABLE_INSTRUCTION_METERING != 0
- int instructions_left = -1;
+ int64 instructions_left = INT64_MAX;
+ int64 const *instructions_schedule = def_instructions_schedule;
if (exec_env) {
instructions_left = exec_env->instructions_to_execute;
+ instructions_schedule = exec_env->instructions_schedule;
+ }
+#endif
+
}
#endif
#if !defined(OS_ENABLE_HW_BOUND_CHECK) \
|| WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0
#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
@@ -7761,6 +7786,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
@@ -7694,6 +7714,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
{
wasm_interp_call_func_native(module, exec_env, cur_func,
prev_frame);
+ instructions_left -= cur_func->gas;
}
#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0
@@ -7806,6 +7827,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
FREE_FRAME(exec_env, frame);
wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)prev_frame);
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+ if (exec_env)
+ exec_env->instructions_to_execute = instructions_to_execute;
+ exec_env->instructions_to_execute = instructions_left;
+#endif
+
if (!prev_frame->ip)
/* Called from native. */
return;
@@ -7789,6 +7819,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
@@ -7834,6 +7860,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
got_exception:
SYNC_ALL_TO_FRAME();
+#if WASM_ENABLE_INSTRUCTION_METERING != 0
+ if (exec_env)
+ exec_env->instructions_to_execute = instructions_to_execute;
+ exec_env->instructions_to_execute = instructions_left;
+#endif
return;
#if WASM_ENABLE_LABELS_AS_VALUES == 0
diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h
index 8d38c883..a687ab89 100644
--- a/core/iwasm/interpreter/wasm_runtime.h
+++ b/core/iwasm/interpreter/wasm_runtime.h
@@ -228,6 +228,10 @@ struct WASMFunctionInstance {
WASMFunctionImport *func_import;
WASMFunction *func;
} u;
+
+ // gas cost for import func
+ uint32 gas;
+
#if WASM_ENABLE_MULTI_MODULE != 0
WASMModuleInstance *import_module_inst;
WASMFunctionInstance *import_func_inst;
diff --git a/core/shared/platform/include/platform_wasi_types.h b/core/shared/platform/include/platform_wasi_types.h
index ac1a95ea..e23b500e 100644
--- a/core/shared/platform/include/platform_wasi_types.h
@@ -453,18 +502,3 @@ index ac1a95ea..e23b500e 100644
#endif
assert_wasi_layout(_Alignof(int8_t) == 1, "non-wasi data layout");
diff --git a/doc/build_wamr.md b/doc/build_wamr.md
index 6425450b..94dd9628 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:

View File

@@ -23,7 +23,7 @@
#include <xrpld/app/misc/WasmHostFunc.h>
#include <xrpld/app/misc/WasmVM.h>
#include <iwasm/wasm_c_api.h>
#include <wasm_c_api.h>
#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
@@ -71,97 +71,6 @@ getLedgerSqn_wrap(void* env, wasm_val_vec_t const*, wasm_val_vec_t* results)
return nullptr;
}
struct TestHostFunctionsOld : public HostFunctions
{
test::jtx::Env* env_;
Bytes accountID_;
Bytes data_;
int clock_drift_ = 0;
test::StreamSink sink_;
beast::Journal jlog_;
void const* rt_ = nullptr;
public:
explicit TestHostFunctionsOld(test::jtx::Env* env, int cd = 0)
: env_(env)
, clock_drift_(cd)
, sink_(beast::severities::kDebug)
, jlog_(sink_)
{
std::string s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
accountID_ = Bytes{s.begin(), s.end()};
std::string t = "10000";
data_ = Bytes{t.begin(), t.end()};
}
virtual void
setRT(void const* rt) override
{
rt_ = rt;
}
virtual void const*
getRT() const override
{
return rt_;
}
test::StreamSink&
getSink()
{
return sink_;
}
beast::Journal
getJournal() override
{
return jlog_;
}
int32_t
getLedgerSqn() override
{
return (int32_t)env_->current()->seq();
}
int32_t
getParentLedgerTime() override
{
return env_->current()->parentCloseTime().time_since_epoch().count() +
clock_drift_;
}
Expected<Bytes, int32_t>
getTxField(SField const& fname) override
{
return accountID_;
}
Expected<Bytes, int32_t>
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
{
return data_;
}
Expected<Bytes, int32_t>
getCurrentLedgerObjField(SField const& fname) override
{
if (fname.getName() == "Destination" || fname.getName() == "Account")
return accountID_;
else if (fname.getName() == "Data")
return data_;
else if (fname.getName() == "FinishAfter")
{
auto t =
env_->current()->parentCloseTime().time_since_epoch().count();
std::string s = std::to_string(t);
return Bytes{s.begin(), s.end()};
}
return Unexpected(-1);
}
};
struct TestHostFunctions : public HostFunctions
{
test::jtx::Env& env_;
@@ -171,7 +80,7 @@ struct TestHostFunctions : public HostFunctions
void const* rt_ = nullptr;
public:
explicit TestHostFunctions(test::jtx::Env& env, int cd = 0)
TestHostFunctions(test::jtx::Env& env, int cd = 0)
: env_(env), clock_drift_(cd)
{
auto opt = parseBase58<AccountID>("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh");
@@ -212,6 +121,12 @@ public:
clock_drift_;
}
Hash
getParentLedgerHash() override
{
return env_.current()->info().parentHash;
}
virtual int32_t
cacheLedgerObj(Keylet const& keylet, int32_t cacheIdx) override
{
@@ -238,29 +153,6 @@ public:
return Bytes();
}
Expected<Bytes, int32_t>
getTxNestedField(Slice const& locator) override
{
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
return Bytes(&a[0], &a[sizeof(a)]);
}
Expected<Bytes, int32_t>
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
{
// auto const& sn = fname.getName();
if (fname == sfBalance)
{
int64_t x = 10'000;
uint8_t const* p = reinterpret_cast<uint8_t const*>(&x);
return Bytes{p, p + sizeof(x)};
}
return data_;
}
Expected<Bytes, int32_t>
getCurrentLedgerObjField(SField const& fname) override
{
@@ -280,24 +172,97 @@ public:
return Unexpected(-1);
}
Expected<Bytes, int32_t>
getLedgerObjField(int32_t cacheIdx, SField const& fname) override
{
// auto const& sn = fname.getName();
if (fname == sfBalance)
{
int64_t x = 10'000;
uint8_t const* p = reinterpret_cast<uint8_t const*>(&x);
return Bytes{p, p + sizeof(x)};
}
return data_;
}
Expected<Bytes, int32_t>
getTxNestedField(Slice const& locator) override
{
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
return Bytes(&a[0], &a[sizeof(a)]);
}
Expected<Bytes, int32_t>
getCurrentLedgerObjNestedField(Slice const& locator) override
{
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
return Bytes(&a[0], &a[sizeof(a)]);
}
Expected<Bytes, int32_t>
getLedgerObjNestedField(int32_t cacheIdx, Slice const& locator) override
{
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41,
0xbf, 0x49, 0xd2, 0x45, 0x9f, 0xa4, 0xa0, 0x34,
0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92, 0xfc, 0xee,
0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
return Bytes(&a[0], &a[sizeof(a)]);
}
int32_t
getTxArrayLen(SField const& fname) override
{
return 32;
}
int32_t
getCurrentLedgerObjArrayLen(SField const& fname) override
{
return 32;
}
int32_t
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) override
{
return 32;
}
int32_t
getTxNestedArrayLen(Slice const& locator) override
{
return 32;
}
int32_t
getCurrentLedgerObjNestedArrayLen(Slice const& locator) override
{
return 32;
}
int32_t
getLedgerObjNestedArrayLen(int32_t cacheIdx, Slice const& locator) override
{
return 32;
}
int32_t
updateData(Bytes const& data) override
{
return 0;
}
Hash
computeSha512HalfHash(Bytes const& data) override
{
return env_.current()->info().parentHash;
}
Expected<Bytes, int32_t>
accountKeylet(AccountID const& account) override
{
@@ -307,6 +272,49 @@ public:
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, int32_t>
credentialKeylet(
AccountID const& subject,
AccountID const& issuer,
Bytes const& credentialType) override
{
if (!subject || !issuer || credentialType.empty())
return Unexpected(HF_ERR_INVALID_ACCOUNT);
auto const keylet =
keylet::credential(subject, issuer, makeSlice(credentialType));
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, int32_t>
escrowKeylet(AccountID const& account, std::uint32_t seq) override
{
if (!account)
return Unexpected(HF_ERR_INVALID_ACCOUNT);
auto const keylet = keylet::escrow(account, seq);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, int32_t>
oracleKeylet(AccountID const& account, std::uint32_t documentId) override
{
if (!account)
return Unexpected(HF_ERR_INVALID_ACCOUNT);
auto const keylet = keylet::oracle(account, documentId);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
Expected<Bytes, int32_t>
getNFT(AccountID const& account, uint256 const& nftId) override
{
if (!account || !nftId)
{
return Unexpected(HF_ERR_INVALID_PARAMS);
}
std::string s = "https://ripple.com";
return Bytes(s.begin(), s.end());
}
int32_t
trace(std::string const& msg, Bytes const& data, bool asHex) override
{
@@ -350,6 +358,33 @@ public:
}
};
struct TestHostFunctionsSink : public TestHostFunctions
{
test::StreamSink sink_;
beast::Journal jlog_;
void const* rt_ = nullptr;
public:
explicit TestHostFunctionsSink(test::jtx::Env& env, int cd = 0)
: TestHostFunctions(env, cd)
, sink_(beast::severities::kDebug)
, jlog_(sink_)
{
}
test::StreamSink&
getSink()
{
return sink_;
}
beast::Journal
getJournal() override
{
return jlog_;
}
};
struct Wasm_test : public beast::unit_test::suite
{
void
@@ -361,9 +396,9 @@ struct Wasm_test : public beast::unit_test::suite
Bytes const wasm(ws.begin(), ws.end());
auto& engine = WasmEngine::instance();
auto const r = engine.run(wasm, "fib", wasmParams(10));
auto const re = engine.run(wasm, "fib", wasmParams(10));
BEAST_EXPECT(r.has_value() && (r->result == 55));
BEAST_EXPECT(re.has_value() && (re->result == 55) && (re->cost == 755));
}
void
@@ -375,10 +410,11 @@ struct Wasm_test : public beast::unit_test::suite
Bytes const wasm(ws.begin(), ws.end());
auto& engine = WasmEngine::instance();
auto const r =
auto const re =
engine.run(wasm, "sha512_process", wasmParams(sha512PureHex));
BEAST_EXPECT(r.has_value() && (r->result == 34432));
BEAST_EXPECT(
re.has_value() && (re->result == 34432) && (re->cost == 157'452));
}
void
@@ -396,9 +432,9 @@ struct Wasm_test : public beast::unit_test::suite
static_cast<std::uint32_t>(512),
static_cast<std::uint32_t>(b58Hex.size()));
auto const s = std::string_view(b58Hex.c_str(), minsz);
auto const r = engine.run(wasm, "b58enco", wasmParams(outb, s));
auto const re = engine.run(wasm, "b58enco", wasmParams(outb, s));
BEAST_EXPECT(r.has_value() && r->result);
BEAST_EXPECT(re.has_value() && re->result && (re->cost == 3'066'129));
}
void
@@ -409,9 +445,10 @@ struct Wasm_test : public beast::unit_test::suite
Bytes const wasm(ws.begin(), ws.end());
auto& engine = WasmEngine::instance();
auto const r = engine.run(wasm, "sp1_groth16_verifier");
auto const re = engine.run(wasm, "sp1_groth16_verifier");
BEAST_EXPECT(r.has_value() && r->result);
BEAST_EXPECT(
re.has_value() && re->result && (re->cost == 4'191'711'969ll));
}
void
@@ -422,9 +459,9 @@ struct Wasm_test : public beast::unit_test::suite
Bytes const wasm(ws.begin(), ws.end());
auto& engine = WasmEngine::instance();
auto const r = engine.run(wasm, "bellman_groth16_test");
auto const re = engine.run(wasm, "bellman_groth16_test");
BEAST_EXPECT(r.has_value() && r->result);
BEAST_EXPECT(re.has_value() && re->result && (re->cost == 332'205'984));
}
void
@@ -442,24 +479,29 @@ struct Wasm_test : public beast::unit_test::suite
std::string const funcName("finish");
std::vector<WasmImportFunc> imports;
WASM_IMPORT_FUNC(imports, getLedgerSqn, &ledgerDataProvider);
WASM_IMPORT_FUNC(imports, getLedgerSqn, &ledgerDataProvider, 33);
auto& engine = WasmEngine::instance();
auto r = engine.run(
auto re = engine.run(
wasm, funcName, {}, imports, nullptr, 1'000'000, env.journal);
if (BEAST_EXPECT(r.has_value()))
BEAST_EXPECT(!r->result);
// code takes 4 gas + 1 getLedgerSqn call
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(!re->result && (re->cost == 37));
env.close();
env.close();
env.close();
env.close();
// empty module - run the same module
r = engine.run(
// empty module - run the same instance
re = engine.run(
{}, funcName, {}, imports, nullptr, 1'000'000, env.journal);
if (BEAST_EXPECT(r.has_value()))
BEAST_EXPECT(r->result);
// code takes 8 gas + 2 getLedgerSqn calls
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(re->result && (re->cost == 74));
}
void
@@ -479,7 +521,7 @@ struct Wasm_test : public beast::unit_test::suite
auto re = runEscrowWasm(
wasm, funcName, wasmParams(data), nullptr, -1, env.journal);
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(re.value().result);
BEAST_EXPECT(re.value().result && (re->cost == 838));
}
{
std::string str = "rHb9CJAWyB4rj91VRWn96DkukG4bwdty00";
@@ -487,7 +529,7 @@ struct Wasm_test : public beast::unit_test::suite
auto re = runEscrowWasm(
wasm, funcName, wasmParams(data), nullptr, -1, env.journal);
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(!re.value().result);
BEAST_EXPECT(!re.value().result && (re->cost == 822));
}
}
@@ -513,7 +555,7 @@ struct Wasm_test : public beast::unit_test::suite
-1,
env.journal);
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(re.value().result);
BEAST_EXPECT(re.value().result && (re->cost == 42'212));
std::vector<uint8_t> const lo_data2(lo_js2.begin(), lo_js2.end());
re = runEscrowWasm(
@@ -524,7 +566,7 @@ struct Wasm_test : public beast::unit_test::suite
-1,
env.journal);
if (BEAST_EXPECT(re.has_value()))
BEAST_EXPECT(!re.value().result);
BEAST_EXPECT(!re.value().result && (re->cost == 41'496));
}
void
@@ -566,11 +608,11 @@ struct Wasm_test : public beast::unit_test::suite
WasmImpFunc<Add_proto>(
imports, "func-add", reinterpret_cast<void*>(&Add));
auto res = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports);
auto re = vm.run(wasm, "addTwo", wasmParams(1234, 5678), imports);
// if (res) printf("invokeAdd get the result: %d\n", res.value());
BEAST_EXPECT(res.has_value() && res->result == 6912);
BEAST_EXPECT(re.has_value() && re->result == 6912 && (re->cost == 2));
}
void
@@ -587,7 +629,7 @@ struct Wasm_test : public beast::unit_test::suite
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
std::string funcName("mock_escrow");
auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal);
BEAST_EXPECT(re.error());
BEAST_EXPECT(!re && re.error());
}
void
@@ -616,10 +658,10 @@ struct Wasm_test : public beast::unit_test::suite
{
TestHostFunctions nfs(env, 0);
std::string funcName("finish");
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000);
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re.value().result);
BEAST_EXPECT(!re->result && (re->cost == 487));
// std::cout << "good case result " << re.value().result
// << " cost: " << re.value().cost << std::endl;
}
@@ -636,7 +678,7 @@ struct Wasm_test : public beast::unit_test::suite
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(!re.value().result);
BEAST_EXPECT(!re->result && (re->cost == 487));
// std::cout << "bad case (current time < escrow_finish_after "
// "time) result "
// << re.value().result << " cost: " <<
@@ -654,13 +696,13 @@ struct Wasm_test : public beast::unit_test::suite
Expected<Bytes, int32_t>
getTxField(SField const& fname) override
{
return Unexpected(-1);
return Unexpected(HF_ERR_FIELD_NOT_FOUND);
}
};
BadTestHostFunctions nfs(env);
std::string funcName("finish");
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
BEAST_EXPECT(re.error());
BEAST_EXPECT(re.has_value() && !re->result && (re->cost == 23));
// std::cout << "bad case (access nonexistent field) result "
// << re.error() << std::endl;
}
@@ -680,7 +722,7 @@ struct Wasm_test : public beast::unit_test::suite
BadTestHostFunctions nfs(env);
std::string funcName("finish");
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100000);
BEAST_EXPECT(!re);
BEAST_EXPECT(re.has_value() && !re->result && (re->cost == 23));
// std::cout << "bad case (more than MAX_PAGES) result "
// << re.error() << std::endl;
}
@@ -690,10 +732,10 @@ struct Wasm_test : public beast::unit_test::suite
auto wasmStr = boost::algorithm::unhex(std::string(wasmHex));
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
TestHostFunctionsOld nfs(&env);
TestHostFunctionsSink nfs(env);
std::string funcName("recursive");
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 1000'000'000);
BEAST_EXPECT(re.error());
BEAST_EXPECT(!re && re.error());
// std::cout << "bad case (deep recursion) result " << re.error()
// << std::endl;
@@ -712,13 +754,32 @@ struct Wasm_test : public beast::unit_test::suite
BEAST_EXPECT(
countSubstr(
sink.messages().str(), "WAMR error: failed to call func") ==
sink.messages().str(), "WAMR Error: failed to call func") ==
1);
BEAST_EXPECT(
countSubstr(
sink.messages().str(),
"WAMR Exception: wasm operand stack overflow") == 1);
}
{
auto wasmStr = boost::algorithm::unhex(ledgerSqnHex);
Bytes wasm(wasmStr.begin(), wasmStr.end());
std::string const funcName("finish");
TestLedgerDataProvider ledgerDataProvider(&env);
std::vector<WasmImportFunc> imports;
WASM_IMPORT_FUNC2(
imports, getLedgerSqn, "get_ledger_sqn2", &ledgerDataProvider);
auto& engine = WasmEngine::instance();
auto re = engine.run(
wasm, funcName, {}, imports, nullptr, 1'000'000, env.journal);
// expected import not provided
BEAST_EXPECT(!re);
}
}
void
@@ -741,7 +802,7 @@ struct Wasm_test : public beast::unit_test::suite
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000);
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result);
BEAST_EXPECT(re->result && (re->cost == 80));
// std::cout << "good case result " << re.value().result
// << " cost: " << re.value().cost << std::endl;
}
@@ -763,7 +824,7 @@ struct Wasm_test : public beast::unit_test::suite
auto re = runEscrowWasm(wasm, funcName, {}, &nfs, 100'000);
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result);
BEAST_EXPECT(re->result && (re->cost == 138));
// std::cout << "good case result " << re.value().result
// << " cost: " << re.value().cost << std::endl;
}
@@ -787,10 +848,10 @@ struct Wasm_test : public beast::unit_test::suite
// runing too long
// testWasmSP1Verifier();
// testWasmBG16Verifier();
testWasmBG16Verifier();
// TODO: needs fix for new host functions interface
// testEscrowWasmDN1();
// TODO: fix result
testEscrowWasmDN1();
testEscrowWasmDN2();
}
};

View File

@@ -529,6 +529,15 @@ ModuleWrapper::buildImports(
reinterpret_cast<wasm_func_callback_with_env_t>(imp.wrap),
imp.udata,
nullptr);
if (!func)
throw std::runtime_error(
"can't create import function " +
imp.name); // LCOV_EXCL_LINE
if (imp.gas && !wasm_func_set_gas(func, imp.gas))
throw std::runtime_error(
"can't set gas for import function " +
imp.name); // LCOV_EXCL_LINE
wimports.data[i] = wasm_func_as_extern(func);
++impCnt;
@@ -862,6 +871,10 @@ WamrEngine::runHlp(
// auto j = j_.debug();
// #endif
// currently only 1 module support, possible parallel UT run
static std::mutex m;
std::lock_guard<decltype(m)> lg(m);
// Create and instantiate the module.
if (!wasmCode.empty())
{
@@ -894,7 +907,8 @@ WamrEngine::runHlp(
}
assert(res.r.data[0].kind == WASM_I32);
if (gas == -1)
gas = std::numeric_limits<decltype(gas)>::max();
WasmResult<int32_t> const ret{res.r.data[0].of.i32, gas - module->getGas()};
// j << "WAMR Res: " << ret.result << " cost: " << ret.cost << std::endl;

View File

@@ -20,8 +20,8 @@
#include <xrpld/app/misc/WasmVM.h>
#include <iwasm/wasm_c_api.h>
#include <iwasm/wasm_export.h>
#include <wasm_c_api.h>
#include <wasm_export.h>
namespace ripple {

View File

@@ -62,6 +62,7 @@ struct WasmImportFunc
void* udata = nullptr;
// wasm_func_callback_with_env_t
void* wrap = nullptr;
uint32_t gas = 0;
};
#define WASM_IMPORT_FUNC(v, f, ...) \
@@ -139,12 +140,14 @@ WasmImpFunc(
std::vector<WasmImportFunc>& v,
std::string_view imp_name,
void* f_wrap,
void* data = nullptr)
void* data = nullptr,
uint32_t gas = 0)
{
WasmImportFunc e;
e.name = imp_name;
e.udata = data;
e.wrap = f_wrap;
e.gas = gas;
WasmImpFuncHelper<F>(e);
v.push_back(std::move(e));
}