From 562a5dd1b688b1517491cccc3dd635b5a58ca562 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Sat, 18 Nov 2023 09:50:16 +0900 Subject: [PATCH] Fix data/elem drop (#2747) Currently, `data.drop` instruction is implemented by directly modifying the underlying module. It breaks use cases where you have multiple instances sharing a single loaded module. `elem.drop` has the same problem too. This PR fixes the issue by keeping track of which data/elem segments have been dropped by using bitmaps for each module instances separately, and add a sample to demonstrate the issue and make the CI run it. Also add a missing check of dropped elements to the fast-jit `table.init`. Fixes: https://github.com/bytecodealliance/wasm-micro-runtime/issues/2735 Fixes: https://github.com/bytecodealliance/wasm-micro-runtime/issues/2772 --- .../compilation_on_android_ubuntu.yml | 6 + .github/workflows/compilation_on_macos.yml | 6 + .github/workflows/nightly_run.yml | 6 + core/iwasm/aot/aot_loader.c | 1 - core/iwasm/aot/aot_runtime.c | 74 +++++-- core/iwasm/compilation/aot.c | 1 - core/iwasm/compilation/aot.h | 1 - core/iwasm/compilation/aot_emit_aot_file.c | 2 +- core/iwasm/fast-jit/fe/jit_emit_memory.c | 36 +-- core/iwasm/fast-jit/fe/jit_emit_table.c | 27 ++- core/iwasm/interpreter/wasm.h | 1 - core/iwasm/interpreter/wasm_interp_classic.c | 25 ++- core/iwasm/interpreter/wasm_interp_fast.c | 27 ++- core/iwasm/interpreter/wasm_runtime.c | 59 ++++- core/iwasm/interpreter/wasm_runtime.h | 7 + core/shared/utils/bh_bitmap.c | 27 +++ core/shared/utils/bh_bitmap.h | 114 ++++++++++ product-mini/platforms/alios-things/aos.mk | 1 + product-mini/platforms/nuttx/wamr.mk | 1 + samples/shared-module/.gitignore | 1 + samples/shared-module/CMakeLists.txt | 95 ++++++++ samples/shared-module/README.md | 5 + samples/shared-module/build.sh | 63 ++++++ samples/shared-module/run.sh | 3 + samples/shared-module/src/main.c | 206 ++++++++++++++++++ samples/shared-module/wasm-apps/testapp.wat | 22 ++ 26 files changed, 745 insertions(+), 72 deletions(-) create mode 100644 core/shared/utils/bh_bitmap.c create mode 100644 core/shared/utils/bh_bitmap.h create mode 100644 samples/shared-module/.gitignore create mode 100644 samples/shared-module/CMakeLists.txt create mode 100644 samples/shared-module/README.md create mode 100755 samples/shared-module/build.sh create mode 100755 samples/shared-module/run.sh create mode 100644 samples/shared-module/src/main.c create mode 100644 samples/shared-module/wasm-apps/testapp.wat diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index 6b653b501..98d346f9e 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -444,6 +444,12 @@ jobs: cmake --build . --config Release --parallel 4 ./iwasm wasm-apps/no_pthread.wasm + - name: Build Sample [shared-module] + run: | + cd samples/shared-module + ./build.sh + ./run.sh + test: needs: [ diff --git a/.github/workflows/compilation_on_macos.yml b/.github/workflows/compilation_on_macos.yml index aac16898b..12f1d73cb 100644 --- a/.github/workflows/compilation_on_macos.yml +++ b/.github/workflows/compilation_on_macos.yml @@ -327,3 +327,9 @@ jobs: cmake .. cmake --build . --config Release --parallel 4 ./iwasm wasm-apps/no_pthread.wasm + + - name: Build Sample [shared-module] + run: | + cd samples/shared-module + ./build.sh + ./run.sh diff --git a/.github/workflows/nightly_run.yml b/.github/workflows/nightly_run.yml index 7b85f2cda..8153cea99 100644 --- a/.github/workflows/nightly_run.yml +++ b/.github/workflows/nightly_run.yml @@ -501,6 +501,12 @@ jobs: cmake .. cmake --build . --config Release --parallel 4 ./iwasm wasm-apps/no_pthread.wasm + + - name: Build Sample [shared-module] + run: | + cd samples/shared-module + ./build.sh + ./run.sh test: needs: [ diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index 37084ba8e..abffd6438 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -1095,7 +1095,6 @@ load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end, data_list[i]->mode = mode; data_list[i]->elem_type = elem_type; - data_list[i]->is_dropped = false; data_list[i]->table_index = table_index; data_list[i]->offset.init_expr_type = (uint8)init_expr_type; data_list[i]->offset.u.i64 = (int64)init_expr_value; diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index f747cb43f..9912f63f0 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -1098,6 +1098,9 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent, char *error_buf, uint32 error_buf_size) { AOTModuleInstance *module_inst; +#if WASM_ENABLE_BULK_MEMORY != 0 || WASM_ENABLE_REF_TYPES != 0 + WASMModuleInstanceExtraCommon *common; +#endif const uint32 module_inst_struct_size = offsetof(AOTModuleInstance, global_table_data.bytes); const uint64 module_inst_mem_inst_size = @@ -1164,6 +1167,32 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent, } #endif +#if WASM_ENABLE_BULK_MEMORY != 0 || WASM_ENABLE_REF_TYPES != 0 + common = &((AOTModuleInstanceExtra *)module_inst->e)->common; +#endif +#if WASM_ENABLE_BULK_MEMORY != 0 + if (module->mem_init_data_count > 0) { + common->data_dropped = bh_bitmap_new(0, module->mem_init_data_count); + if (common->data_dropped == NULL) { + LOG_DEBUG("failed to allocate bitmaps"); + set_error_buf(error_buf, error_buf_size, + "failed to allocate bitmaps"); + goto fail; + } + } +#endif +#if WASM_ENABLE_REF_TYPES != 0 + if (module->table_init_data_count > 0) { + common->elem_dropped = bh_bitmap_new(0, module->table_init_data_count); + if (common->elem_dropped == NULL) { + LOG_DEBUG("failed to allocate bitmaps"); + set_error_buf(error_buf, error_buf_size, + "failed to allocate bitmaps"); + goto fail; + } + } +#endif + /* Initialize global info */ p = (uint8 *)module_inst + module_inst_struct_size + module_inst_mem_inst_size; @@ -1264,6 +1293,8 @@ fail: void aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst) { + WASMModuleInstanceExtraCommon *common = + &((AOTModuleInstanceExtra *)module_inst->e)->common; if (module_inst->exec_env_singleton) { /* wasm_exec_env_destroy will call wasm_cluster_wait_for_all_except_self to wait for other @@ -1306,7 +1337,7 @@ aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst) if (module_inst->func_type_indexes) wasm_runtime_free(module_inst->func_type_indexes); - if (((AOTModuleInstanceExtra *)module_inst->e)->common.c_api_func_imports) + if (common->c_api_func_imports) wasm_runtime_free(((AOTModuleInstanceExtra *)module_inst->e) ->common.c_api_func_imports); @@ -1317,6 +1348,13 @@ aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst) wasm_native_call_context_dtors((WASMModuleInstanceCommon *)module_inst); } +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_bitmap_delete(common->data_dropped); +#endif +#if WASM_ENABLE_REF_TYPES != 0 + bh_bitmap_delete(common->elem_dropped); +#endif + wasm_runtime_free(module_inst); } @@ -2302,13 +2340,21 @@ aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index, uint32 offset, { AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); AOTModule *aot_module; - uint8 *data = NULL; + uint8 *data; uint8 *maddr; - uint64 seg_len = 0; + uint64 seg_len; - aot_module = (AOTModule *)module_inst->module; - seg_len = aot_module->mem_init_data_list[seg_index]->byte_count; - data = aot_module->mem_init_data_list[seg_index]->bytes; + if (bh_bitmap_get_bit( + ((AOTModuleInstanceExtra *)module_inst->e)->common.data_dropped, + seg_index)) { + seg_len = 0; + data = NULL; + } + else { + aot_module = (AOTModule *)module_inst->module; + seg_len = aot_module->mem_init_data_list[seg_index]->byte_count; + data = aot_module->mem_init_data_list[seg_index]->bytes; + } if (!wasm_runtime_validate_app_addr((WASMModuleInstanceCommon *)module_inst, dst, len)) @@ -2331,9 +2377,9 @@ aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index, uint32 offset, bool aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index) { - AOTModule *aot_module = (AOTModule *)module_inst->module; - - aot_module->mem_init_data_list[seg_index]->byte_count = 0; + bh_bitmap_set_bit( + ((AOTModuleInstanceExtra *)module_inst->e)->common.data_dropped, + seg_index); /* Currently we can't free the dropped data segment as the mem_init_data_count is a continuous array */ return true; @@ -2546,9 +2592,9 @@ aot_get_module_inst_mem_consumption(const AOTModuleInstance *module_inst, void aot_drop_table_seg(AOTModuleInstance *module_inst, uint32 tbl_seg_idx) { - AOTModule *module = (AOTModule *)module_inst->module; - AOTTableInitData *tbl_seg = module->table_init_data_list[tbl_seg_idx]; - tbl_seg->is_dropped = true; + bh_bitmap_set_bit( + ((AOTModuleInstanceExtra *)module_inst->e)->common.elem_dropped, + tbl_seg_idx); } void @@ -2576,7 +2622,9 @@ aot_table_init(AOTModuleInstance *module_inst, uint32 tbl_idx, return; } - if (tbl_seg->is_dropped) { + if (bh_bitmap_get_bit( + ((AOTModuleInstanceExtra *)module_inst->e)->common.elem_dropped, + tbl_seg_idx)) { aot_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); return; } diff --git a/core/iwasm/compilation/aot.c b/core/iwasm/compilation/aot.c index e836df28f..8162d006e 100644 --- a/core/iwasm/compilation/aot.c +++ b/core/iwasm/compilation/aot.c @@ -129,7 +129,6 @@ aot_create_table_init_data_list(const WASMModule *module) data_list[i]->mode = module->table_segments[i].mode; data_list[i]->elem_type = module->table_segments[i].elem_type; /* runtime control it */ - data_list[i]->is_dropped = false; data_list[i]->table_index = module->table_segments[i].table_index; bh_memcpy_s(&data_list[i]->offset, sizeof(AOTInitExpr), &module->table_segments[i].base_offset, diff --git a/core/iwasm/compilation/aot.h b/core/iwasm/compilation/aot.h index 088460636..4bee70f9c 100644 --- a/core/iwasm/compilation/aot.h +++ b/core/iwasm/compilation/aot.h @@ -143,7 +143,6 @@ typedef struct AOTTableInitData { uint32 mode; /* funcref or externref, elemkind will be considered as funcref */ uint32 elem_type; - bool is_dropped; /* optional, only for active */ uint32 table_index; /* Start address of init data */ diff --git a/core/iwasm/compilation/aot_emit_aot_file.c b/core/iwasm/compilation/aot_emit_aot_file.c index 103b2750e..80bcdc7db 100644 --- a/core/iwasm/compilation/aot_emit_aot_file.c +++ b/core/iwasm/compilation/aot_emit_aot_file.c @@ -269,7 +269,7 @@ static uint32 get_table_init_data_size(AOTTableInitData *table_init_data) { /* - * mode (4 bytes), elem_type (4 bytes), do not need is_dropped field + * mode (4 bytes), elem_type (4 bytes) * * table_index(4 bytes) + init expr type (4 bytes) + init expr value (8 * bytes) diff --git a/core/iwasm/fast-jit/fe/jit_emit_memory.c b/core/iwasm/fast-jit/fe/jit_emit_memory.c index 9635d4e57..ce3f77fae 100644 --- a/core/iwasm/fast-jit/fe/jit_emit_memory.c +++ b/core/iwasm/fast-jit/fe/jit_emit_memory.c @@ -650,6 +650,7 @@ wasm_init_memory(WASMModuleInstance *inst, uint32 mem_idx, uint32 seg_idx, WASMDataSeg *data_segment; uint32 mem_size; uint8 *mem_addr, *data_addr; + uint32 seg_len; /* if d + n > the length of mem.data */ mem_inst = inst->memories[mem_idx]; @@ -659,13 +660,19 @@ wasm_init_memory(WASMModuleInstance *inst, uint32 mem_idx, uint32 seg_idx, /* if s + n > the length of data.data */ bh_assert(seg_idx < inst->module->data_seg_count); - data_segment = inst->module->data_segments[seg_idx]; - if (data_segment->data_length < data_offset - || data_segment->data_length - data_offset < len) + if (bh_bitmap_get_bit(inst->e->common.data_dropped, seg_idx)) { + seg_len = 0; + data_addr = NULL; + } + else { + data_segment = inst->module->data_segments[seg_idx]; + seg_len = data_segment->data_length; + data_addr = data_segment->data + data_offset; + } + if (seg_len < data_offset || seg_len - data_offset < len) goto out_of_bounds; mem_addr = mem_inst->memory_data + mem_offset; - data_addr = data_segment->data + data_offset; bh_memcpy_s(mem_addr, mem_size - mem_offset, data_addr, len); return 0; @@ -706,21 +713,22 @@ fail: return false; } +static void +wasm_data_drop(WASMModuleInstance *inst, uint32 seg_idx) +{ + bh_bitmap_set_bit(inst->e->common.data_dropped, seg_idx); +} + bool jit_compile_op_data_drop(JitCompContext *cc, uint32 seg_idx) { - JitReg module = get_module_reg(cc->jit_frame); - JitReg data_segments = jit_cc_new_reg_ptr(cc); - JitReg data_segment = jit_cc_new_reg_ptr(cc); + JitReg args[2] = { 0 }; - GEN_INSN(LDPTR, data_segments, module, - NEW_CONST(I32, offsetof(WASMModule, data_segments))); - GEN_INSN(LDPTR, data_segment, data_segments, - NEW_CONST(I32, seg_idx * sizeof(WASMDataSeg *))); - GEN_INSN(STI32, NEW_CONST(I32, 0), data_segment, - NEW_CONST(I32, offsetof(WASMDataSeg, data_length))); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, seg_idx); - return true; + return jit_emit_callnative(cc, wasm_data_drop, 0, args, + sizeof(args) / sizeof(args[0])); } static int diff --git a/core/iwasm/fast-jit/fe/jit_emit_table.c b/core/iwasm/fast-jit/fe/jit_emit_table.c index ea1b33883..b8ed6a1d5 100644 --- a/core/iwasm/fast-jit/fe/jit_emit_table.c +++ b/core/iwasm/fast-jit/fe/jit_emit_table.c @@ -10,21 +10,22 @@ #include "../jit_frontend.h" #if WASM_ENABLE_REF_TYPES != 0 +static void +wasm_elem_drop(WASMModuleInstance *inst, uint32 tbl_seg_idx) +{ + bh_bitmap_set_bit(inst->e->common.elem_dropped, tbl_seg_idx); +} + bool jit_compile_op_elem_drop(JitCompContext *cc, uint32 tbl_seg_idx) { - JitReg module, tbl_segs; + JitReg args[2] = { 0 }; - module = get_module_reg(cc->jit_frame); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, tbl_seg_idx); - tbl_segs = jit_cc_new_reg_ptr(cc); - GEN_INSN(LDPTR, tbl_segs, module, - NEW_CONST(I32, offsetof(WASMModule, table_segments))); - - GEN_INSN(STI32, NEW_CONST(I32, true), tbl_segs, - NEW_CONST(I32, tbl_seg_idx * sizeof(WASMTableSeg) - + offsetof(WASMTableSeg, is_dropped))); - return true; + return jit_emit_callnative(cc, wasm_elem_drop, 0, args, + sizeof(args) / sizeof(args[0])); } bool @@ -105,6 +106,12 @@ wasm_init_table(WASMModuleInstance *inst, uint32 tbl_idx, uint32 elem_idx, if (offset_len_out_of_bounds(dst_offset, len, tbl_sz)) goto out_of_bounds; + if (!len) + return 0; + + if (bh_bitmap_get_bit(inst->e->common.elem_dropped, elem_idx)) + goto out_of_bounds; + bh_memcpy_s((uint8 *)tbl + offsetof(WASMTableInstance, elems) + dst_offset * sizeof(uint32), (uint32)((tbl_sz - dst_offset) * sizeof(uint32)), diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 89b0bc741..ee537aa63 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -311,7 +311,6 @@ typedef struct WASMTableSeg { uint32 mode; /* funcref or externref, elemkind will be considered as funcref */ uint32 elem_type; - bool is_dropped; /* optional, only for active */ uint32 table_index; InitializerExpression base_offset; diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index ec28e7baf..72a81ea84 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -3160,9 +3160,17 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, maddr = memory->memory_data + (uint32)addr; #endif - seg_len = (uint64)module->module->data_segments[segment] - ->data_length; - data = module->module->data_segments[segment]->data; + if (bh_bitmap_get_bit(module->e->common.data_dropped, + segment)) { + seg_len = 0; + data = NULL; + } + else { + seg_len = + (uint64)module->module->data_segments[segment] + ->data_length; + data = module->module->data_segments[segment]->data; + } if (offset + bytes > seg_len) goto out_of_bounds; @@ -3175,7 +3183,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 segment; read_leb_uint32(frame_ip, frame_ip_end, segment); - module->module->data_segments[segment]->data_length = 0; + bh_bitmap_set_bit(module->e->common.data_dropped, + segment); break; } case WASM_OP_MEMORY_COPY: @@ -3270,8 +3279,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, break; } - if (module->module->table_segments[elem_idx] - .is_dropped) { + if (bh_bitmap_get_bit(module->e->common.elem_dropped, + elem_idx)) { wasm_set_exception(module, "out of bounds table access"); goto got_exception; @@ -3303,8 +3312,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, read_leb_uint32(frame_ip, frame_ip_end, elem_idx); bh_assert(elem_idx < module->module->table_seg_count); - module->module->table_segments[elem_idx].is_dropped = - true; + bh_bitmap_set_bit(module->e->common.elem_dropped, + elem_idx); break; } case WASM_OP_TABLE_COPY: diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 82b6e747a..b0d0c6004 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -3005,10 +3005,18 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, goto out_of_bounds; maddr = memory->memory_data + (uint32)addr; #endif + if (bh_bitmap_get_bit(module->e->common.data_dropped, + segment)) { + seg_len = 0; + data = NULL; + } + else { - seg_len = (uint64)module->module->data_segments[segment] - ->data_length; - data = module->module->data_segments[segment]->data; + seg_len = + (uint64)module->module->data_segments[segment] + ->data_length; + data = module->module->data_segments[segment]->data; + } if (offset + bytes > seg_len) goto out_of_bounds; @@ -3021,8 +3029,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 segment; segment = read_uint32(frame_ip); - - module->module->data_segments[segment]->data_length = 0; + bh_bitmap_set_bit(module->e->common.data_dropped, + segment); break; } case WASM_OP_MEMORY_COPY: @@ -3114,8 +3122,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, break; } - if (module->module->table_segments[elem_idx] - .is_dropped) { + if (bh_bitmap_get_bit(module->e->common.elem_dropped, + elem_idx)) { wasm_set_exception(module, "out of bounds table access"); goto got_exception; @@ -3144,9 +3152,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, { uint32 elem_idx = read_uint32(frame_ip); bh_assert(elem_idx < module->module->table_seg_count); - - module->module->table_segments[elem_idx].is_dropped = - true; + bh_bitmap_set_bit(module->e->common.elem_dropped, + elem_idx); break; } case WASM_OP_TABLE_COPY: diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 720a78f03..9efeb14c0 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -1666,6 +1666,31 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, } #endif +#if WASM_ENABLE_BULK_MEMORY != 0 + if (module->data_seg_count > 0) { + module_inst->e->common.data_dropped = + bh_bitmap_new(0, module->data_seg_count); + if (module_inst->e->common.data_dropped == NULL) { + LOG_DEBUG("failed to allocate bitmaps"); + set_error_buf(error_buf, error_buf_size, + "failed to allocate bitmaps"); + goto fail; + } + } +#endif +#if WASM_ENABLE_REF_TYPES != 0 + if (module->table_seg_count > 0) { + module_inst->e->common.elem_dropped = + bh_bitmap_new(0, module->table_seg_count); + if (module_inst->e->common.elem_dropped == NULL) { + LOG_DEBUG("failed to allocate bitmaps"); + set_error_buf(error_buf, error_buf_size, + "failed to allocate bitmaps"); + goto fail; + } + } +#endif + #if WASM_ENABLE_DUMP_CALL_STACK != 0 if (!(module_inst->frames = runtime_malloc((uint64)sizeof(Vector), error_buf, error_buf_size))) { @@ -2189,6 +2214,13 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) wasm_native_call_context_dtors((WASMModuleInstanceCommon *)module_inst); } +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_bitmap_delete(module_inst->e->common.data_dropped); +#endif +#if WASM_ENABLE_REF_TYPES != 0 + bh_bitmap_delete(module_inst->e->common.elem_dropped); +#endif + wasm_runtime_free(module_inst); } @@ -3148,16 +3180,23 @@ llvm_jit_memory_init(WASMModuleInstance *module_inst, uint32 seg_index, { WASMMemoryInstance *memory_inst; WASMModule *module; - uint8 *data = NULL; + uint8 *data; uint8 *maddr; - uint64 seg_len = 0; + uint64 seg_len; bh_assert(module_inst->module_type == Wasm_Module_Bytecode); memory_inst = wasm_get_default_memory(module_inst); - module = module_inst->module; - seg_len = module->data_segments[seg_index]->data_length; - data = module->data_segments[seg_index]->data; + + if (bh_bitmap_get_bit(module_inst->e->common.data_dropped, seg_index)) { + seg_len = 0; + data = NULL; + } + else { + module = module_inst->module; + seg_len = module->data_segments[seg_index]->data_length; + data = module->data_segments[seg_index]->data; + } if (!wasm_runtime_validate_app_addr((WASMModuleInstanceCommon *)module_inst, dst, len)) @@ -3182,7 +3221,7 @@ llvm_jit_data_drop(WASMModuleInstance *module_inst, uint32 seg_index) { bh_assert(module_inst->module_type == Wasm_Module_Bytecode); - module_inst->module->data_segments[seg_index]->data_length = 0; + bh_bitmap_set_bit(module_inst->e->common.data_dropped, seg_index); /* Currently we can't free the dropped data segment as they are stored in wasm bytecode */ return true; @@ -3193,12 +3232,8 @@ llvm_jit_data_drop(WASMModuleInstance *module_inst, uint32 seg_index) void llvm_jit_drop_table_seg(WASMModuleInstance *module_inst, uint32 tbl_seg_idx) { - WASMTableSeg *tbl_segs; - bh_assert(module_inst->module_type == Wasm_Module_Bytecode); - - tbl_segs = module_inst->module->table_segments; - tbl_segs[tbl_seg_idx].is_dropped = true; + bh_bitmap_set_bit(module_inst->e->common.elem_dropped, tbl_seg_idx); } void @@ -3227,7 +3262,7 @@ llvm_jit_table_init(WASMModuleInstance *module_inst, uint32 tbl_idx, return; } - if (tbl_seg->is_dropped) { + if (bh_bitmap_get_bit(module_inst->e->common.elem_dropped, tbl_seg_idx)) { jit_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); return; } diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 7d78df14f..1bd51551f 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -8,6 +8,7 @@ #include "wasm.h" #include "bh_atomic.h" +#include "bh_bitmap.h" #include "bh_hashmap.h" #include "../common/wasm_runtime_common.h" #include "../common/wasm_exec_env.h" @@ -223,6 +224,12 @@ typedef struct WASMModuleInstanceExtraCommon { /* Disable bounds checks or not */ bool disable_bounds_checks; #endif +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_bitmap *data_dropped; +#endif +#if WASM_ENABLE_REF_TYPES != 0 + bh_bitmap *elem_dropped; +#endif } WASMModuleInstanceExtraCommon; /* Extra info of WASM module instance for interpreter/jit mode */ diff --git a/core/shared/utils/bh_bitmap.c b/core/shared/utils/bh_bitmap.c new file mode 100644 index 000000000..2ee918049 --- /dev/null +++ b/core/shared/utils/bh_bitmap.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_bitmap.h" + +bh_bitmap * +bh_bitmap_new(uintptr_t begin_index, unsigned bitnum) +{ + bh_bitmap *bitmap; + uint32 bitmap_size = (bitnum + 7) / 8; + uint32 total_size = offsetof(bh_bitmap, map) + bitmap_size; + + if (bitnum > UINT32_MAX - 7 || total_size < offsetof(bh_bitmap, map) + || (total_size - offsetof(bh_bitmap, map)) != bitmap_size) { + return NULL; /* integer overflow */ + } + + if ((bitmap = BH_MALLOC(total_size)) != NULL) { + memset(bitmap, 0, total_size); + bitmap->begin_index = begin_index; + bitmap->end_index = begin_index + bitnum; + } + + return bitmap; +} diff --git a/core/shared/utils/bh_bitmap.h b/core/shared/utils/bh_bitmap.h new file mode 100644 index 000000000..fe7251480 --- /dev/null +++ b/core/shared/utils/bh_bitmap.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_BITMAP_H +#define _BH_BITMAP_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A simple fixed size bitmap. + */ +typedef struct bh_bitmap { + /* The first valid bit index. */ + uintptr_t begin_index; + + /* The last valid bit index plus one. */ + uintptr_t end_index; + + /* The bitmap. */ + uint8 map[1]; +} bh_bitmap; + +/** + * Create a new bitmap. + * + * @param begin_index the first valid bit index + * @param bitnum maximal bit number of the bitmap. + * + * @return the new bitmap if succeeds, NULL otherwise. + */ +bh_bitmap * +bh_bitmap_new(uintptr_t begin_index, unsigned bitnum); + +/** + * Delete a bitmap. + * + * @param bitmap the bitmap to be deleted + */ +static inline void +bh_bitmap_delete(bh_bitmap *bitmap) +{ + if (bitmap != NULL) + BH_FREE(bitmap); +} + +/** + * Check whether the given index is in the range of the bitmap. + * + * @param bitmap the bitmap + * @param n the bit index + * + * @return true if the index is in range, false otherwise + */ +static inline bool +bh_bitmap_is_in_range(bh_bitmap *bitmap, unsigned n) +{ + return n >= bitmap->begin_index && n < bitmap->end_index; +} + +/** + * Get a bit in the bitmap + * + * @param bitmap the bitmap + * @param n the n-th bit to be get + * + * @return value of the bit + */ +static inline int +bh_bitmap_get_bit(bh_bitmap *bitmap, unsigned n) +{ + unsigned idx = n - bitmap->begin_index; + bh_assert(n >= bitmap->begin_index && n < bitmap->end_index); + return (bitmap->map[idx / 8] >> (idx % 8)) & 1; +} + +/** + * Set a bit in the bitmap. + * + * @param bitmap the bitmap + * @param n the n-th bit to be set + */ +static inline void +bh_bitmap_set_bit(bh_bitmap *bitmap, unsigned n) +{ + unsigned idx = n - bitmap->begin_index; + bh_assert(n >= bitmap->begin_index && n < bitmap->end_index); + bitmap->map[idx / 8] |= 1 << (idx % 8); +} + +/** + * Clear a bit in the bitmap. + * + * @param bitmap the bitmap + * @param n the n-th bit to be cleared + */ +static inline void +bh_bitmap_clear_bit(bh_bitmap *bitmap, unsigned n) +{ + unsigned idx = n - bitmap->begin_index; + bh_assert(n >= bitmap->begin_index && n < bitmap->end_index); + bitmap->map[idx / 8] &= ~(1 << (idx % 8)); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/product-mini/platforms/alios-things/aos.mk b/product-mini/platforms/alios-things/aos.mk index 383e0b239..3f25cb980 100644 --- a/product-mini/platforms/alios-things/aos.mk +++ b/product-mini/platforms/alios-things/aos.mk @@ -98,6 +98,7 @@ $(NAME)_SOURCES := ${SHARED_ROOT}/platform/alios/alios_platform.c \ ${SHARED_ROOT}/mem-alloc/ems/ems_alloc.c \ ${SHARED_ROOT}/mem-alloc/ems/ems_hmu.c \ ${SHARED_ROOT}/utils/bh_assert.c \ + ${SHARED_ROOT}/utils/bh_bitmap.c \ ${SHARED_ROOT}/utils/bh_common.c \ ${SHARED_ROOT}/utils/bh_hashmap.c \ ${SHARED_ROOT}/utils/bh_list.c \ diff --git a/product-mini/platforms/nuttx/wamr.mk b/product-mini/platforms/nuttx/wamr.mk index ef90f14da..8bd05f48d 100644 --- a/product-mini/platforms/nuttx/wamr.mk +++ b/product-mini/platforms/nuttx/wamr.mk @@ -371,6 +371,7 @@ CSRCS += nuttx_platform.c \ ems_alloc.c \ ems_hmu.c \ bh_assert.c \ + bh_bitmap.c \ bh_common.c \ bh_hashmap.c \ bh_list.c \ diff --git a/samples/shared-module/.gitignore b/samples/shared-module/.gitignore new file mode 100644 index 000000000..0fa8a76bd --- /dev/null +++ b/samples/shared-module/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/samples/shared-module/CMakeLists.txt b/samples/shared-module/CMakeLists.txt new file mode 100644 index 000000000..c094b071c --- /dev/null +++ b/samples/shared-module/CMakeLists.txt @@ -0,0 +1,95 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +project (shared-module) + +set (CMAKE_CXX_STANDARD 17) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Debug) +endif () + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) + +# fast interpreter +# set (WAMR_BUILD_FAST_INTERP 1) + +# fast-jit +# set (WAMR_BUILD_FAST_JIT 1) + +# llvm jit +# set (WAMR_BUILD_JIT 1) +# set (LLVM_DIR /usr/local/opt/llvm@14/lib/cmake/llvm) + +set (WAMR_BUILD_REF_TYPES 1) + +if (NOT MSVC) + # linker flags + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + endif () + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () + endif () +endif () + +# build out vmlib +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (shared-module src/main.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (shared-module PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (shared-module vmlib -lm -ldl -lpthread ${LLVM_AVAILABLE_LIBS}) +else () + target_link_libraries (shared-module vmlib -lm -ldl -lpthread -lrt ${LLVM_AVAILABLE_LIBS}) +endif () diff --git a/samples/shared-module/README.md b/samples/shared-module/README.md new file mode 100644 index 000000000..14baa8cbf --- /dev/null +++ b/samples/shared-module/README.md @@ -0,0 +1,5 @@ +The "shared-module" sample project +================================== + +This sample demonstrates a bug described in: +https://github.com/bytecodealliance/wasm-micro-runtime/issues/2735. diff --git a/samples/shared-module/build.sh b/samples/shared-module/build.sh new file mode 100755 index 000000000..9af5b3edd --- /dev/null +++ b/samples/shared-module/build.sh @@ -0,0 +1,63 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "##################### build shared-module project" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. +make -j ${nproc} +if [ $? != 0 ];then + echo "BUILD_FAIL shared-module exit as $?\n" + exit 2 +fi + +cp -a shared-module ${OUT_DIR} + +printf "\n" + +echo "##################### build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.wat` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# Note: the CI installs wabt in /opt/wabt +if type wat2wasm; then + WAT2WASM=${WAT2WASM:-wat2wasm} +elif [ -x /opt/wabt/bin/wat2wasm ]; then + WAT2WASM=${WAT2WASM:-/opt/wabt/bin/wat2wasm} +fi + +${WAT2WASM} -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + +# aot +# wamrc -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot ${OUT_DIR}/wasm-apps/${OUT_FILE} +# mv ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot ${OUT_DIR}/wasm-apps/${OUT_FILE} + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "##################### build wasm apps done" diff --git a/samples/shared-module/run.sh b/samples/shared-module/run.sh new file mode 100755 index 000000000..3cb2e623e --- /dev/null +++ b/samples/shared-module/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +out/shared-module -f out/wasm-apps/testapp.wasm diff --git a/samples/shared-module/src/main.c b/samples/shared-module/src/main.c new file mode 100644 index 000000000..ebea0c6bf --- /dev/null +++ b/samples/shared-module/src/main.c @@ -0,0 +1,206 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "bh_getopt.h" + +void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + +int +main(int argc, char *argv_main[]) +{ + int exit_code = 1; + static char global_heap_buf[512 * 1024]; + char *buffer; + char error_buf[128]; + int opt; + char *wasm_path = NULL; + + const unsigned int N = 4; + wasm_module_t module = NULL; + wasm_module_inst_t module_inst[N]; + wasm_exec_env_t exec_env[N]; + const char *name_test_data_drop = "test_data_drop"; + const char *name_test_elem_drop = "test_elem_drop"; + wasm_function_inst_t func_test_data_drop[N]; + wasm_function_inst_t func_test_elem_drop[N]; + unsigned int i; + unsigned int iter; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + + for (i = 0; i < N; i++) { + module_inst[i] = NULL; + exec_env[i] = NULL; + func_test_data_drop[i] = NULL; + func_test_elem_drop[i] = NULL; + } + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + memset(&init_args, 0, sizeof(init_args)); + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + for (i = 0; i < N; i++) { + module_inst[i] = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + + if (!module_inst[i]) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + exec_env[i] = wasm_runtime_create_exec_env(module_inst[i], stack_size); + if (!exec_env[i]) { + printf("Create wasm execution environment failed.\n"); + goto fail; + } + + func_test_data_drop[i] = wasm_runtime_lookup_function( + module_inst[i], name_test_data_drop, NULL); + if (!func_test_data_drop[i]) { + printf("The wasm function %s is not found.\n", name_test_data_drop); + goto fail; + } + + func_test_elem_drop[i] = wasm_runtime_lookup_function( + module_inst[i], name_test_elem_drop, NULL); + if (!func_test_elem_drop[i]) { + printf("The wasm function %s is not found.\n", name_test_elem_drop); + goto fail; + } + } + + for (iter = 0; iter < 2; iter++) { + /* + * as we drop data/table in the first iteration, + * the later iterations should trap. + */ + const bool should_trap = iter > 0; + + for (i = 0; i < N; i++) { + uint32_t argv[1] = {}; + if (wasm_runtime_call_wasm(exec_env[i], func_test_data_drop[i], 0, + argv)) { + uint32_t result = argv[0]; + printf( + "Native finished calling wasm function: %s, return: %x\n", + name_test_data_drop, result); + if (result != 0x64636261) { /* "abcd" */ + printf("unexpected return value\n"); + goto fail; + } + if (should_trap) { + printf("a trap is expected\n"); + goto fail; + } + } + else if (should_trap) { + printf("call wasm function %s failed as expected. error: %s\n", + name_test_data_drop, + wasm_runtime_get_exception(module_inst[i])); + } + else { + printf("call wasm function %s failed. error: %s\n", + name_test_data_drop, + wasm_runtime_get_exception(module_inst[i])); + goto fail; + } + } + + for (i = 0; i < N; i++) { + wasm_runtime_clear_exception(module_inst[i]); + + uint32_t argv[1] = {}; + if (wasm_runtime_call_wasm(exec_env[i], func_test_elem_drop[i], 0, + argv)) { + uint32_t result = argv[0]; + printf( + "Native finished calling wasm function: %s, return: %x\n", + name_test_elem_drop, result); + if (result != 0) { + printf("unexpected return value\n"); + goto fail; + } + if (should_trap) { + printf("a trap is expected\n"); + goto fail; + } + } + else if (should_trap) { + printf("call wasm function %s failed as expected. error: %s\n", + name_test_elem_drop, + wasm_runtime_get_exception(module_inst[i])); + } + else { + printf("call wasm function %s failed. error: %s\n", + name_test_elem_drop, + wasm_runtime_get_exception(module_inst[i])); + goto fail; + } + } + } + + exit_code = 0; +fail: + for (i = 0; i < N; i++) { + if (exec_env[i]) + wasm_runtime_destroy_exec_env(exec_env[i]); + if (module_inst[i]) + wasm_runtime_deinstantiate(module_inst[i]); + } + if (module) + wasm_runtime_unload(module); + if (buffer) + BH_FREE(buffer); + wasm_runtime_destroy(); + return exit_code; +} diff --git a/samples/shared-module/wasm-apps/testapp.wat b/samples/shared-module/wasm-apps/testapp.wat new file mode 100644 index 000000000..263a87036 --- /dev/null +++ b/samples/shared-module/wasm-apps/testapp.wat @@ -0,0 +1,22 @@ +;; Copyright (C) 2023 Midokura Japan KK. All rights reserved. +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +(module + (func (export "test_data_drop") (result i32) + (memory.init 0 (i32.const 0) (i32.const 0) (i32.const 4)) + data.drop 0 + (i32.load (i32.const 0)) + ) + (func (export "test_elem_drop") (result i32) + (table.init 0 (i32.const 0) (i32.const 0) (i32.const 4)) + elem.drop 0 + i32.const 3 + table.get 0 + ref.is_null + ) + (func $f) + (memory 1 1) + (table 4 4 funcref) + (data "abcd") + (elem func $f $f $f $f) +)