Add WASI ABI compatibility check for multi-module (#913)
Refer to https://github.com/WebAssembly/WASI/blob/main/design/application-abi.md to check the WASI ABI compatibility: - Command (main module) may export _start function with signature "()" - Reactor (sub module) may export _initialize function with signature "()" - _start and _initialize can not be exported at the same time - Reactor cannot export _start function - Command and Reactor must export memory And - Rename module->is_wasi_module to module->import_wasi_api - Refactor wasm_loader_find_export() - Remove MULTI_MODULE related codes from mini_loader - Update multi-module samples - Fix a "use-after-free" issue. Since we reuse the memory instance of sub module, just to protect it from freeing an imported memory instance
This commit is contained in:
@ -647,11 +647,11 @@ adjust_table_max_size(uint32 init_size, uint32 max_size_flag, uint32 *max_size)
|
||||
static WASMExport *
|
||||
wasm_loader_find_export(const WASMModule *module, const char *module_name,
|
||||
const char *field_name, uint8 export_kind,
|
||||
uint32 export_index_boundary, char *error_buf,
|
||||
uint32 error_buf_size)
|
||||
char *error_buf, uint32 error_buf_size)
|
||||
{
|
||||
WASMExport *export;
|
||||
uint32 i;
|
||||
uint32 export_index_boundary = 0;
|
||||
|
||||
for (i = 0, export = module->exports; i < module->export_count;
|
||||
++i, ++export) {
|
||||
@ -674,6 +674,27 @@ wasm_loader_find_export(const WASMModule *module, const char *module_name,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (export_kind) {
|
||||
case EXPORT_KIND_FUNC:
|
||||
export_index_boundary =
|
||||
module->import_function_count + module->function_count;
|
||||
break;
|
||||
case EXPORT_KIND_GLOBAL:
|
||||
export_index_boundary =
|
||||
module->import_global_count + module->global_count;
|
||||
break;
|
||||
case EXPORT_KIND_MEMORY:
|
||||
export_index_boundary =
|
||||
module->import_memory_count + module->memory_count;
|
||||
break;
|
||||
case EXPORT_KIND_TABLE:
|
||||
export_index_boundary =
|
||||
module->import_table_count + module->table_count;
|
||||
break;
|
||||
default:
|
||||
bh_assert(0);
|
||||
}
|
||||
|
||||
if (export->index >= export_index_boundary) {
|
||||
LOG_DEBUG("%s in the module %s is out of index (%d >= %d )", field_name,
|
||||
module_name, export->index, export_index_boundary);
|
||||
@ -704,10 +725,9 @@ wasm_loader_resolve_function(const char *module_name, const char *function_name,
|
||||
}
|
||||
|
||||
module = (WASMModule *)module_reg;
|
||||
export = wasm_loader_find_export(
|
||||
module, module_name, function_name, EXPORT_KIND_FUNC,
|
||||
module->import_function_count + module->function_count, error_buf,
|
||||
error_buf_size);
|
||||
export =
|
||||
wasm_loader_find_export(module, module_name, function_name,
|
||||
EXPORT_KIND_FUNC, error_buf, error_buf_size);
|
||||
if (!export) {
|
||||
return NULL;
|
||||
}
|
||||
@ -755,10 +775,9 @@ wasm_loader_resolve_table(const char *module_name, const char *table_name,
|
||||
}
|
||||
|
||||
module = (WASMModule *)module_reg;
|
||||
export = wasm_loader_find_export(
|
||||
module, module_name, table_name, EXPORT_KIND_TABLE,
|
||||
module->table_count + module->import_table_count, error_buf,
|
||||
error_buf_size);
|
||||
export =
|
||||
wasm_loader_find_export(module, module_name, table_name,
|
||||
EXPORT_KIND_TABLE, error_buf, error_buf_size);
|
||||
if (!export) {
|
||||
return NULL;
|
||||
}
|
||||
@ -800,10 +819,9 @@ wasm_loader_resolve_memory(const char *module_name, const char *memory_name,
|
||||
}
|
||||
|
||||
module = (WASMModule *)module_reg;
|
||||
export = wasm_loader_find_export(
|
||||
module, module_name, memory_name, EXPORT_KIND_MEMORY,
|
||||
module->import_memory_count + module->memory_count, error_buf,
|
||||
error_buf_size);
|
||||
export =
|
||||
wasm_loader_find_export(module, module_name, memory_name,
|
||||
EXPORT_KIND_MEMORY, error_buf, error_buf_size);
|
||||
if (!export) {
|
||||
return NULL;
|
||||
}
|
||||
@ -846,10 +864,9 @@ wasm_loader_resolve_global(const char *module_name, const char *global_name,
|
||||
}
|
||||
|
||||
module = (WASMModule *)module_reg;
|
||||
export = wasm_loader_find_export(
|
||||
module, module_name, global_name, EXPORT_KIND_GLOBAL,
|
||||
module->import_global_count + module->global_count, error_buf,
|
||||
error_buf_size);
|
||||
export =
|
||||
wasm_loader_find_export(module, module_name, global_name,
|
||||
EXPORT_KIND_GLOBAL, error_buf, error_buf_size);
|
||||
if (!export) {
|
||||
return NULL;
|
||||
}
|
||||
@ -969,7 +986,7 @@ load_depended_module(const WASMModule *parent_module,
|
||||
}
|
||||
|
||||
sub_module =
|
||||
wasm_loader_load(buffer, buffer_size, error_buf, error_buf_size);
|
||||
wasm_loader_load(buffer, buffer_size, false, error_buf, error_buf_size);
|
||||
if (!sub_module) {
|
||||
LOG_DEBUG("error: can not load the sub_module %s", sub_module_name);
|
||||
/* others will be destroyed in runtime_destroy() */
|
||||
@ -1736,7 +1753,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
|
||||
if (!strcmp(import->u.names.module_name, "wasi_unstable")
|
||||
|| !strcmp(import->u.names.module_name,
|
||||
"wasi_snapshot_preview1")) {
|
||||
module->is_wasi_module = true;
|
||||
module->import_wasi_api = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -3440,9 +3457,129 @@ fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (WASM_ENABLE_MULTI_MODULE != 0) && (WASM_ENABLE_LIBC_WASI != 0)
|
||||
/*
|
||||
* refer to
|
||||
* https://github.com/WebAssembly/WASI/blob/main/design/application-abi.md
|
||||
*/
|
||||
static bool
|
||||
check_wasi_abi_compatibility(const WASMModule *module, bool main_module,
|
||||
char *error_buf, uint32 error_buf_size)
|
||||
{
|
||||
/*
|
||||
* need to handle:
|
||||
* - non-wasi compatiable modules
|
||||
* - a fake wasi compatiable module
|
||||
* - a command acts as a main_module
|
||||
* - a command acts as a sub_module
|
||||
* - a reactor acts as a main_module
|
||||
* - a reactor acts as a sub_module
|
||||
*
|
||||
* be careful with:
|
||||
* wasi compatiable modules(command/reactor) which don't import any wasi
|
||||
* APIs. usually, a command has to import a "prox_exit" at least. but a
|
||||
* reactor can depend on nothing. At the same time, each has its own entry
|
||||
* point.
|
||||
*
|
||||
* observations:
|
||||
* - clang always injects `_start` into a command
|
||||
* - clang always injects `_initialize` into a reactor
|
||||
* - `iwasm -f` allows to run a function in the reactor
|
||||
*
|
||||
* strong assumptions:
|
||||
* - no one will define either `_start` or `_initialize` on purpose
|
||||
* - `_start` should always be `void _start(void)`
|
||||
* - `_initialize` should always be `void _initialize(void)`
|
||||
*/
|
||||
|
||||
WASMExport *initialize = NULL, *memory = NULL, *start = NULL;
|
||||
|
||||
/* (func (export "_start") (...) */
|
||||
start = wasm_loader_find_export(module, "", "_start", EXPORT_KIND_FUNC,
|
||||
error_buf, error_buf_size);
|
||||
if (start) {
|
||||
WASMType *func_type =
|
||||
module->functions[start->index - module->import_function_count]
|
||||
->func_type;
|
||||
if (func_type->param_count || func_type->result_count) {
|
||||
set_error_buf(error_buf, error_buf_size,
|
||||
"The builtin _start() is with a wrong signature");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* (func (export "_initialize") (...) */
|
||||
initialize = wasm_loader_find_export(
|
||||
module, "", "_initialize", EXPORT_KIND_FUNC, error_buf, error_buf_size);
|
||||
if (initialize) {
|
||||
WASMType *func_type =
|
||||
module->functions[initialize->index - module->import_function_count]
|
||||
->func_type;
|
||||
if (func_type->param_count || func_type->result_count) {
|
||||
set_error_buf(
|
||||
error_buf, error_buf_size,
|
||||
"The builtin _initiazlie() is with a wrong signature");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* filter out non-wasi compatiable modules */
|
||||
if (!module->import_wasi_api && !start && !initialize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* should have one at least */
|
||||
if (module->import_wasi_api && !start && !initialize) {
|
||||
set_error_buf(
|
||||
error_buf, error_buf_size,
|
||||
"A module with WASI apis should be either a command or a reactor");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* there is at least one of `_start` and `_initialize` in below cases.
|
||||
* according to the assumption, they should be all wasi compatiable
|
||||
*/
|
||||
|
||||
/* always can not have both at the same time */
|
||||
if (start && initialize) {
|
||||
set_error_buf(
|
||||
error_buf, error_buf_size,
|
||||
"Neither a command nor a reactor can have both at the same time");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* filter out commands (with `_start`) cases */
|
||||
if (start && !main_module) {
|
||||
set_error_buf(error_buf, error_buf_size,
|
||||
"A command(with _start) can not be a sud-module");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* it is ok a reactor acts as a main module,
|
||||
* so skip the check about (with `_initialize`)
|
||||
*/
|
||||
|
||||
memory = wasm_loader_find_export(module, "", "memory", EXPORT_KIND_MEMORY,
|
||||
error_buf, error_buf_size);
|
||||
if (!memory) {
|
||||
set_error_buf(
|
||||
error_buf, error_buf_size,
|
||||
"A module with WASI apis should export memory by default");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
WASMModule *
|
||||
wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf,
|
||||
uint32 error_buf_size)
|
||||
wasm_loader_load(const uint8 *buf, uint32 size,
|
||||
#if WASM_ENABLE_MULTI_MODULE != 0
|
||||
bool main_module,
|
||||
#endif
|
||||
char *error_buf, uint32 error_buf_size)
|
||||
{
|
||||
WASMModule *module = create_module(error_buf, error_buf_size);
|
||||
if (!module) {
|
||||
@ -3458,6 +3595,14 @@ wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#if (WASM_ENABLE_MULTI_MODULE != 0) && (WASM_ENABLE_LIBC_WASI != 0)
|
||||
/* do a check about WASI Application ABI */
|
||||
if (!check_wasi_abi_compatibility(module, main_module, error_buf,
|
||||
error_buf_size)) {
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_VERBOSE("Load module success.\n");
|
||||
return module;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user