diff --git a/include/modules/wasi.h b/include/modules/wasi.h index 09991d8..3bf75c4 100644 --- a/include/modules/wasi.h +++ b/include/modules/wasi.h @@ -6,6 +6,8 @@ #include bool wasi_module_has_wasi_imports(void *c_api_module); +bool wasi_module_is_command_or_reactor(void *c_api_module); +bool wasi_bytes_need_wasi_command_warning_suppression(const uint8_t *wasm_bytes, size_t wasm_len); ant_value_t wasi_instantiate( ant_t *js, const uint8_t *wasm_bytes, size_t wasm_len, diff --git a/src/modules/wasi.c b/src/modules/wasi.c index 1b1d1d2..ac3ff2e 100644 --- a/src/modules/wasi.c +++ b/src/modules/wasi.c @@ -35,6 +35,181 @@ typedef struct { wasm_function_inst_t func; } wasi_func_env_t; +enum { + WASM_SECTION_IMPORT = 2, + WASM_SECTION_EXPORT = 7, +}; + +static bool wasm_read_u32_leb(const uint8_t *buf, size_t len, size_t *offset, uint32_t *out) { + uint32_t value = 0; + uint32_t shift = 0; + + while (*offset < len && shift < 35) { + uint8_t byte = buf[(*offset)++]; + value |= (uint32_t)(byte & 0x7f) << shift; + + if ((byte & 0x80) == 0) { + *out = value; + return true; + } + + shift += 7; + } + + return false; +} + +static bool wasm_read_name(const uint8_t *buf, size_t len, size_t *offset, const uint8_t **data, uint32_t *name_len) { + uint32_t size = 0; + + if ( + !wasm_read_u32_leb(buf, len, offset, &size) + || *offset + size > len + ) return false; + + *data = buf + *offset; + *name_len = size; + *offset += size; + + return true; +} + +static bool wasm_name_equals(const uint8_t *data, uint32_t len, const char *expected) { + size_t expected_len = strlen(expected); + return len == expected_len && memcmp(data, expected, expected_len) == 0; +} + +static bool wasi_bytes_have_wasi_imports(const uint8_t *wasm_bytes, size_t wasm_len) { + size_t offset = 8; + + if (!wasm_bytes || wasm_len < 8 || memcmp(wasm_bytes, "\0asm\x01\0\0\0", 8) != 0) + return false; + + while (offset < wasm_len) { + uint8_t section_id = wasm_bytes[offset++]; + uint32_t section_size = 0; + const uint8_t *section; + size_t section_offset = 0; + size_t section_len; + + if ( + !wasm_read_u32_leb(wasm_bytes, wasm_len, &offset, §ion_size) + || offset + section_size > wasm_len + ) return false; + + section = wasm_bytes + offset; + section_len = section_size; + offset += section_size; + + if (section_id != WASM_SECTION_IMPORT) continue; + if (!wasm_read_u32_leb(section, section_size, §ion_offset, §ion_size)) + return false; + + for (uint32_t i = 0; i < section_size; i++) { + const uint8_t *module_name = NULL; + const uint8_t *field_name = NULL; + uint32_t module_name_len = 0; + uint32_t field_name_len = 0; + uint32_t discard = 0; + uint32_t flags = 0; + + if (!wasm_read_name(section, section_len, §ion_offset, &module_name, &module_name_len) + || !wasm_read_name(section, section_len, §ion_offset, &field_name, &field_name_len) + || section_offset >= section_len) + return false; + + if (wasm_name_equals(module_name, module_name_len, "wasi_snapshot_preview1")) + return true; + + switch (section[section_offset++]) { + case 0: + if (!wasm_read_u32_leb(section, section_len, §ion_offset, &discard)) + return false; + break; + case 1: { + if (section_offset >= section_len) return false; + section_offset++; + if (!wasm_read_u32_leb(section, section_len, §ion_offset, &flags) + || !wasm_read_u32_leb(section, section_len, §ion_offset, &discard)) + return false; + if ((flags & 0x1) != 0 + && !wasm_read_u32_leb(section, section_len, §ion_offset, &discard)) + return false; + break; + } + case 2: + if (!wasm_read_u32_leb(section, section_len, §ion_offset, &flags) + || !wasm_read_u32_leb(section, section_len, §ion_offset, &discard)) + return false; + if ((flags & 0x1) != 0 + && !wasm_read_u32_leb(section, section_len, §ion_offset, &discard)) + return false; + break; + case 3: + if (section_offset + 2 > section_len) return false; + section_offset += 2; + break; + default: + return false; + } + } + } + + return false; +} + +static bool wasi_bytes_have_command_or_reactor_entry(const uint8_t *wasm_bytes, size_t wasm_len) { + size_t offset = 8; + + if (!wasm_bytes || wasm_len < 8 || memcmp(wasm_bytes, "\0asm\x01\0\0\0", 8) != 0) + return false; + + while (offset < wasm_len) { + uint8_t section_id = wasm_bytes[offset++]; + uint32_t section_size = 0; + const uint8_t *section; + size_t section_offset = 0; + size_t section_len; + + if (!wasm_read_u32_leb(wasm_bytes, wasm_len, &offset, §ion_size) + || offset + section_size > wasm_len) + return false; + + section = wasm_bytes + offset; + section_len = section_size; + offset += section_size; + + if (section_id != WASM_SECTION_EXPORT) continue; + if (!wasm_read_u32_leb(section, section_len, §ion_offset, §ion_size)) + return false; + + for (uint32_t i = 0; i < section_size; i++) { + const uint8_t *name = NULL; + uint32_t name_len = 0; + uint32_t discard = 0; + + if (!wasm_read_name(section, section_len, §ion_offset, &name, &name_len) + || section_offset >= section_len) + return false; + + if (section[section_offset++] != 0) { + if (!wasm_read_u32_leb(section, section_len, §ion_offset, &discard)) + return false; + continue; + } + + if (!wasm_read_u32_leb(section, section_len, §ion_offset, &discard)) + return false; + + if (wasm_name_equals(name, name_len, "_start") + || wasm_name_equals(name, name_len, "_initialize")) + return true; + } + } + + return false; +} + static inline bool wasi_is_proc_exit_exception(const char *exception) { return exception != NULL && strstr(exception, "wasi proc exit") != NULL; } @@ -109,6 +284,43 @@ bool wasi_module_has_wasi_imports(void *c_api_module) { return has_wasi; } +bool wasi_module_is_command_or_reactor(void *c_api_module) { + wasm_exporttype_vec_t export_types = {0}; + bool has_entry = false; + + wasm_module_exports((wasm_module_t *)c_api_module, &export_types); + + for (size_t i = 0; i < export_types.size; i++) { + const wasm_name_t *name = wasm_exporttype_name(export_types.data[i]); + const wasm_externtype_t *type = wasm_exporttype_type(export_types.data[i]); + const wasm_functype_t *func_type; + size_t name_len; + + if (!name || wasm_externtype_kind(type) != WASM_EXTERN_FUNC) continue; + name_len = name->size; + if (name_len > 0 && name->data[name_len - 1] == '\0') name_len--; + + if (!((name_len == 6 && memcmp(name->data, "_start", 6) == 0) + || (name_len == 11 && memcmp(name->data, "_initialize", 11) == 0))) + continue; + + func_type = wasm_externtype_as_functype_const(type); + if (!func_type) continue; + if (wasm_functype_params(func_type)->size == 0 && wasm_functype_results(func_type)->size == 0) { + has_entry = true; + break; + } + } + + wasm_exporttype_vec_delete(&export_types); + return has_entry; +} + +bool wasi_bytes_need_wasi_command_warning_suppression(const uint8_t *wasm_bytes, size_t wasm_len) { + return wasi_bytes_have_wasi_imports(wasm_bytes, wasm_len) + && !wasi_bytes_have_command_or_reactor_entry(wasm_bytes, wasm_len); +} + static void wasi_bind_func_export( ant_t *js, ant_value_t exports_obj, ant_value_t instance_obj, wasm_module_inst_t inst, wasm_exec_env_t exec_env, const char *name diff --git a/src/modules/wasm.c b/src/modules/wasm.c index 8be07eb..34ad612 100644 --- a/src/modules/wasm.c +++ b/src/modules/wasm.c @@ -584,7 +584,9 @@ static ant_value_t wasm_module_from_bytes(ant_t *js, ant_value_t value, ant_valu wasm_byte_vec_t binary = WASM_EMPTY_VEC; wasm_store_t *store = NULL; wasm_module_t *module = NULL; + char error_buf[128] = {0}; + bool suppress_wasi_warning = false; *out_module = js_mkundef(); @@ -596,8 +598,16 @@ static ant_value_t wasm_module_from_bytes(ant_t *js, ant_value_t value, ant_valu return js_mkerr_typed(js, JS_ERR_TYPE, "%s", error_buf); } + suppress_wasi_warning = wasi_bytes_need_wasi_command_warning_suppression( + (const uint8_t *)binary.data, binary.size + ); + + if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_ERROR); module = wasm_module_new(store, &binary); + + if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_WARNING); wasm_byte_vec_delete(&binary); + if (!module) { wasm_store_delete(store); return wasm_make_compile_error(js, "Failed to compile WebAssembly module"); @@ -768,12 +778,13 @@ static ant_value_t wasm_instantiate_module(ant_t *js, ant_value_t module_obj, an wasm_instance_t *instance = NULL; ant_value_t instance_obj = js_mkundef(); ant_value_t exports_obj = js_mkobj(js); - *out_instance = js_mkundef(); - if (!module_handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Module"); + if (!module_handle) + return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Module"); - if (wasi_module_has_wasi_imports(module_handle->module)) { + if (wasi_module_has_wasi_imports(module_handle->module) + && wasi_module_is_command_or_reactor(module_handle->module)) { ant_value_t wasi_opts = is_object_type(import_obj) ? js_get(js, import_obj, "wasi") : js_mkundef(); if (!is_object_type(import_obj) || is_object_type(wasi_opts)) { ant_value_t bytes_src = js_get_slot(module_obj, SLOT_MAP); @@ -1205,8 +1216,9 @@ static ant_value_t js_wasm_validate(ant_t *js, ant_value_t *args, int nargs) { wasm_byte_vec_t binary = WASM_EMPTY_VEC; wasm_store_t *store; - char error_buf[128] = {0}; bool ok; + char error_buf[128] = {0}; + bool suppress_wasi_warning = false; if (nargs < 1) return js_false; if (!ensure_wasm_engine()) return js_false; @@ -1217,7 +1229,14 @@ static ant_value_t js_wasm_validate(ant_t *js, ant_value_t *args, int nargs) { return js_false; } + suppress_wasi_warning = wasi_bytes_need_wasi_command_warning_suppression( + (const uint8_t *)binary.data, binary.size + ); + + if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_ERROR); ok = wasm_module_validate(store, &binary); + + if (suppress_wasi_warning) wasm_runtime_set_log_level(WASM_LOG_LEVEL_WARNING); wasm_byte_vec_delete(&binary); wasm_store_delete(store);