Allow finer control of start function on instantiation

Tries to address
https://github.com/bytecodealliance/wasm-micro-runtime/issues/4047.

Before this change, wasm_runtime_instantiate would execute the start
function unconditionally, correctly implementing the spec. However, if
there is an infinite loop in the start function, there was no way to
interrupt execution.

This change introduces a parameter to InstantiationArgs indicating
whether the start function should be immediately invoked. If not,
wasm_runtime_instantiate returns a module instance that is initialized
except for the start function.

This change adds a wasm_runtime_instantiate_run_start_func function
which performs this last instantiation step. The user may prepare a
timeout in advance of calling this to guard against expensive/infinite
loops.

This change also demonstrates a possible technique for doing this in
iwasm.c.
This commit is contained in:
James Ring 2025-02-09 22:15:47 -08:00
parent 7b724e2382
commit b8e57c6ec3
9 changed files with 135 additions and 46 deletions

View File

@ -1622,13 +1622,24 @@ wasm_runtime_instantiate_internal(WASMModuleCommon *module,
WASMModuleInstanceCommon *parent,
WASMExecEnv *exec_env_main, uint32 stack_size,
uint32 heap_size, uint32 max_memory_pages,
char *error_buf, uint32 error_buf_size)
bool run_start_function, char *error_buf,
uint32 error_buf_size)
{
#if WASM_ENABLE_INTERP != 0
if (module->module_type == Wasm_Module_Bytecode)
return (WASMModuleInstanceCommon *)wasm_instantiate(
(WASMModule *)module, (WASMModuleInstance *)parent, exec_env_main,
stack_size, heap_size, max_memory_pages, error_buf, error_buf_size);
if (module->module_type == Wasm_Module_Bytecode) {
if (run_start_function) {
return (WASMModuleInstanceCommon *)wasm_instantiate(
(WASMModule *)module, (WASMModuleInstance *)parent,
exec_env_main, stack_size, heap_size, max_memory_pages,
error_buf, error_buf_size);
}
return (WASMModuleInstanceCommon *)
wasm_instantiate_without_start_function(
(WASMModule *)module, (WASMModuleInstance *)parent,
exec_env_main, stack_size, heap_size, max_memory_pages,
error_buf, error_buf_size);
}
#endif
#if WASM_ENABLE_AOT != 0
if (module->module_type == Wasm_Module_AoT)
@ -1647,7 +1658,7 @@ wasm_runtime_instantiate(WASMModuleCommon *module, uint32 stack_size,
uint32 error_buf_size)
{
return wasm_runtime_instantiate_internal(module, NULL, NULL, stack_size,
heap_size, 0, error_buf,
heap_size, 0, true, error_buf,
error_buf_size);
}
@ -1658,8 +1669,8 @@ wasm_runtime_instantiate_ex(WASMModuleCommon *module,
{
return wasm_runtime_instantiate_internal(
module, NULL, NULL, args->default_stack_size,
args->host_managed_heap_size, args->max_memory_pages, error_buf,
error_buf_size);
args->host_managed_heap_size, args->max_memory_pages,
args->run_start_function, error_buf, error_buf_size);
}
void
@ -7562,7 +7573,7 @@ wasm_runtime_sub_module_instantiate(WASMModuleCommon *module,
WASMModuleInstanceCommon *sub_module_inst = NULL;
sub_module_inst = wasm_runtime_instantiate_internal(
sub_module, NULL, NULL, stack_size, heap_size, max_memory_pages,
error_buf, error_buf_size);
true, error_buf, error_buf_size);
if (!sub_module_inst) {
LOG_DEBUG("instantiate %s failed",
sub_module_list_node->module_name);

View File

@ -575,7 +575,8 @@ wasm_runtime_instantiate_internal(WASMModuleCommon *module,
WASMModuleInstanceCommon *parent,
WASMExecEnv *exec_env_main, uint32 stack_size,
uint32 heap_size, uint32 max_memory_pages,
char *error_buf, uint32 error_buf_size);
bool run_start_function, char *error_buf,
uint32 error_buf_size);
/* Internal API */
void

View File

@ -273,6 +273,8 @@ typedef struct InstantiationArgs {
uint32_t default_stack_size;
uint32_t host_managed_heap_size;
uint32_t max_memory_pages;
bool run_start_function;
} InstantiationArgs;
#endif /* INSTANTIATION_ARGS_OPTION_DEFINED */
@ -719,6 +721,17 @@ wasm_runtime_instantiate_ex(const wasm_module_t module,
const InstantiationArgs *args, char *error_buf,
uint32_t error_buf_size);
/**
* Run the start function of an otherwise instantiated module. See
* InstantiationArgs.run_start_function.
*/
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_instantiate_run_start_func(wasm_module_inst_t module,
wasm_module_inst_t parent,
wasm_exec_env_t exec_env,
char *error_buf,
uint32_t error_buf_size);
/**
* Set the running mode of a WASM module instance, override the
* default running mode of the runtime. Note that it only makes sense when

View File

@ -2333,6 +2333,25 @@ wasm_set_running_mode(WASMModuleInstance *module_inst, RunningMode running_mode)
return set_running_mode(module_inst, running_mode, false);
}
bool
wasm_runtime_instantiate_run_start_func(wasm_module_inst_t module_inst,
wasm_module_inst_t parent,
wasm_exec_env_t exec_env,
char *error_buf,
uint32_t error_buf_size)
{
const bool is_sub_inst = parent != NULL;
if (!execute_post_instantiate_functions((WASMModuleInstance *)module_inst,
is_sub_inst, exec_env)) {
set_error_buf(error_buf, error_buf_size,
((WASMModuleInstance *)module_inst)->cur_exception);
wasm_runtime_deinstantiate(module_inst);
return false;
}
return true;
}
/**
* Instantiate module
*/
@ -2341,6 +2360,41 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
WASMExecEnv *exec_env_main, uint32 stack_size,
uint32 heap_size, uint32 max_memory_pages, char *error_buf,
uint32 error_buf_size)
{
WASMModuleInstance *module_inst = wasm_instantiate_without_start_function(
module, parent, exec_env_main, stack_size, heap_size, max_memory_pages,
error_buf, error_buf_size);
if (!module_inst) {
// wasm_instantiate_without_start_function will deinstantiate on
// failure.
return NULL;
}
if (!wasm_runtime_instantiate_run_start_func(
(WASMModuleInstanceCommon *)module_inst,
(WASMModuleInstanceCommon *)parent, exec_env_main, error_buf,
error_buf_size)) {
// run_start_func will deinstantiate on failure.
set_error_buf(error_buf, error_buf_size, module_inst->cur_exception);
return NULL;
}
#if WASM_ENABLE_MEMORY_TRACING != 0
wasm_runtime_dump_module_inst_mem_consumption(
(WASMModuleInstanceCommon *)module_inst);
#endif
return module_inst;
}
WASMModuleInstance *
wasm_instantiate_without_start_function(WASMModule *module,
WASMModuleInstance *parent,
WASMExecEnv *exec_env_main,
uint32 stack_size, uint32 heap_size,
uint32 max_memory_pages,
char *error_buf, uint32 error_buf_size)
{
WASMModuleInstance *module_inst;
WASMGlobalInstance *globals = NULL, *global;
@ -3257,17 +3311,6 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
&module_inst->e->functions[module->start_function];
}
if (!execute_post_instantiate_functions(module_inst, is_sub_inst,
exec_env_main)) {
set_error_buf(error_buf, error_buf_size, module_inst->cur_exception);
goto fail;
}
#if WASM_ENABLE_MEMORY_TRACING != 0
wasm_runtime_dump_module_inst_mem_consumption(
(WASMModuleInstanceCommon *)module_inst);
#endif
(void)global_data_end;
return module_inst;

View File

@ -543,6 +543,14 @@ bool
wasm_resolve_import_func(const WASMModule *module,
WASMFunctionImport *function);
WASMModuleInstance *
wasm_instantiate_without_start_function(WASMModule *module,
WASMModuleInstance *parent,
WASMExecEnv *exec_env_main,
uint32 stack_size, uint32 heap_size,
uint32 max_memory_pages,
char *error_buf, uint32 error_buf_size);
WASMModuleInstance *
wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
WASMExecEnv *exec_env_main, uint32 stack_size,

View File

@ -580,7 +580,7 @@ pthread_create_wrapper(wasm_exec_env_t exec_env,
#endif
if (!(new_module_inst = wasm_runtime_instantiate_internal(
module, module_inst, exec_env, stack_size, 0, 0, NULL, 0)))
module, module_inst, exec_env, stack_size, 0, 0, true, NULL, 0)))
return -1;
/* Set custom_data to new module instance */

View File

@ -87,7 +87,7 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg)
stack_size = ((WASMModuleInstance *)module_inst)->default_wasm_stack_size;
if (!(new_module_inst = wasm_runtime_instantiate_internal(
module, module_inst, exec_env, stack_size, 0, 0, NULL, 0)))
module, module_inst, exec_env, stack_size, 0, 0, true, NULL, 0)))
return -1;
wasm_runtime_set_custom_data_internal(

View File

@ -506,7 +506,8 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env)
}
if (!(new_module_inst = wasm_runtime_instantiate_internal(
module, module_inst, exec_env, stack_size, 0, 0, NULL, 0))) {
module, module_inst, exec_env, stack_size, 0, 0, true, NULL,
0))) {
return NULL;
}

View File

@ -945,10 +945,38 @@ main(int argc, char *argv[])
libc_wasi_init(wasm_module, argc, argv, &wasi_parse_ctx);
#endif
InstantiationArgs args = {
.default_stack_size = stack_size,
.host_managed_heap_size = heap_size,
.max_memory_pages = 0,
.run_start_function = false,
};
/* instantiate the module */
if (!(wasm_module_inst =
wasm_runtime_instantiate(wasm_module, stack_size, heap_size,
error_buf, sizeof(error_buf)))) {
if (!(wasm_module_inst = wasm_runtime_instantiate_ex(
wasm_module, &args, error_buf, sizeof(error_buf)))) {
printf("%s\n", error_buf);
goto fail3;
}
#if WASM_ENABLE_THREAD_MGR != 0
struct timeout_arg timeout_arg;
korp_tid timeout_tid;
if (timeout_ms >= 0) {
timeout_arg.timeout_ms = timeout_ms;
timeout_arg.inst = wasm_module_inst;
timeout_arg.cancel = false;
ret = os_thread_create(&timeout_tid, timeout_thread, &timeout_arg,
APP_THREAD_STACK_SIZE_DEFAULT);
if (ret != 0) {
printf("Failed to start timeout\n");
goto fail4;
}
}
#endif
if (!wasm_runtime_instantiate_run_start_func(
wasm_module_inst, NULL, NULL, error_buf, sizeof(error_buf))) {
printf("%s\n", error_buf);
goto fail3;
}
@ -966,27 +994,11 @@ main(int argc, char *argv[])
uint32_t debug_port;
if (exec_env == NULL) {
printf("%s\n", wasm_runtime_get_exception(wasm_module_inst));
goto fail4;
goto fail5;
}
debug_port = wasm_runtime_start_debug_instance(exec_env);
if (debug_port == 0) {
printf("Failed to start debug instance\n");
goto fail4;
}
}
#endif
#if WASM_ENABLE_THREAD_MGR != 0
struct timeout_arg timeout_arg;
korp_tid timeout_tid;
if (timeout_ms >= 0) {
timeout_arg.timeout_ms = timeout_ms;
timeout_arg.inst = wasm_module_inst;
timeout_arg.cancel = false;
ret = os_thread_create(&timeout_tid, timeout_thread, &timeout_arg,
APP_THREAD_STACK_SIZE_DEFAULT);
if (ret != 0) {
printf("Failed to start timeout\n");
goto fail5;
}
}
@ -1035,10 +1047,10 @@ main(int argc, char *argv[])
}
#endif
#if WASM_ENABLE_THREAD_MGR != 0
#if WASM_ENABLE_DEBUG_INTERP != 0
fail5:
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
#if WASM_ENABLE_THREAD_MGR != 0
fail4:
#endif
/* destroy the module instance */