Эх сурвалжийг харах

Fix validation non-nullable locals (#2462)

Xu Jun 2 жил өмнө
parent
commit
0bd23ebd8c

+ 19 - 4
core/iwasm/common/wasm_application.c

@@ -501,7 +501,10 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name,
 #endif
 #endif /* WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 */
             default:
+            {
 #if WASM_ENABLE_GC != 0
+                bool is_extern_ref = false;
+
                 if (wasm_is_type_reftype(type->types[i])) {
                     if (strncasecmp(argv[i], "null", 4) == 0) {
                         PUT_REF_TO_ADDR(argv1 + p, NULL_REF);
@@ -509,6 +512,21 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name,
                         break;
                     }
                     else if (type->types[i] == VALUE_TYPE_EXTERNREF) {
+                        is_extern_ref = true;
+                    }
+
+                    if (wasm_is_type_multi_byte_type(
+                            type->types[type->param_count + i])) {
+                        WASMRefType *ref_type = ref_type_map->ref_type;
+                        if (wasm_is_refheaptype_common(&ref_type->ref_ht_common)
+                            && ref_type->ref_ht_common.heap_type
+                                   == HEAP_TYPE_EXTERN) {
+                            is_extern_ref = true;
+                        }
+                        ref_type_map++;
+                    }
+
+                    if (is_extern_ref) {
                         WASMExternrefObjectRef gc_obj;
                         void *extern_obj =
                             (void *)(uintptr_t)strtoull(argv[i], &endptr, 0);
@@ -530,15 +548,12 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name,
                         p += REF_CELL_NUM;
                     }
 
-                    if (wasm_is_type_multi_byte_type(
-                            type->types[type->param_count + i]))
-                        ref_type_map++;
-
                     break;
                 }
 #endif /* end of WASM_ENABLE_GC != 0 */
                 bh_assert(0);
                 break;
+            }
         }
         if (endptr && *endptr != '\0' && *endptr != '_') {
             snprintf(buf, sizeof(buf), "invalid input argument %" PRId32 ": %s",

+ 155 - 3
core/iwasm/interpreter/wasm_loader.c

@@ -6355,6 +6355,10 @@ typedef struct BranchBlock {
     uint32 stack_cell_num;
 #if WASM_ENABLE_GC != 0
     uint32 reftype_map_num;
+    /* Indicate which local is used inside current block, used to validate
+     * local.get with non-nullable ref types */
+    uint8 *local_use_mask;
+    uint32 local_use_mask_size;
 #endif
 #if WASM_ENABLE_FAST_INTERP != 0
     uint16 dynamic_offset;
@@ -6543,6 +6547,107 @@ free_all_label_patch_lists(BranchBlock *frame_csp, uint32 csp_num)
 
 #endif /* end of WASM_ENABLE_FAST_INTERP */
 
+#if WASM_ENABLE_GC != 0
+static bool
+wasm_loader_init_local_use_masks(WASMLoaderContext *ctx, uint32 local_count,
+                                 char *error_buf, uint32 error_buf_size)
+{
+    BranchBlock *current_csp = ctx->frame_csp - 1;
+    uint32 local_mask_size;
+
+    if (local_count == 0) {
+        current_csp->local_use_mask_size = 0;
+        return true;
+    }
+
+    if (current_csp->local_use_mask) {
+        memset(current_csp->local_use_mask, 0,
+               current_csp->local_use_mask_size);
+        return true;
+    }
+
+    local_mask_size = (local_count + 7) / sizeof(uint8);
+    if (!(current_csp->local_use_mask =
+              loader_malloc(local_mask_size, error_buf, error_buf_size))) {
+        return false;
+    }
+    current_csp->local_use_mask_size = local_mask_size;
+
+    if (current_csp->label_type != LABEL_TYPE_FUNCTION) {
+        /* For non-function blocks, inherit the use status from parent block */
+        BranchBlock *parent_csp = current_csp - 1;
+
+        bh_assert(parent_csp >= ctx->frame_csp_bottom);
+        bh_assert(parent_csp->local_use_mask);
+
+        bh_memcpy_s(current_csp->local_use_mask, local_mask_size,
+                    parent_csp->local_use_mask, local_mask_size);
+    }
+
+    return true;
+}
+
+static void
+wasm_loader_destroy_curr_local_use_masks(WASMLoaderContext *ctx)
+{
+    BranchBlock *current_csp = ctx->frame_csp - 1;
+
+    bh_assert(current_csp->local_use_mask
+              || current_csp->local_use_mask_size == 0);
+
+    if (current_csp->local_use_mask) {
+        wasm_runtime_free(current_csp->local_use_mask);
+    }
+
+    current_csp->local_use_mask = NULL;
+    current_csp->local_use_mask_size = 0;
+}
+
+static void
+wasm_loader_clean_all_local_use_masks(WASMLoaderContext *ctx)
+{
+    BranchBlock *tmp_csp = ctx->frame_csp_bottom;
+    uint32 i;
+
+    for (i = 0; i < ctx->csp_num; i++) {
+        if (tmp_csp->local_use_mask) {
+            wasm_runtime_free(tmp_csp->local_use_mask);
+            tmp_csp->local_use_mask = NULL;
+            tmp_csp->local_use_mask_size = 0;
+        }
+        tmp_csp++;
+    }
+}
+
+static void
+wasm_loader_mask_local(WASMLoaderContext *ctx, uint32 index)
+{
+    BranchBlock *current_csp = ctx->frame_csp - 1;
+    uint32 byte_offset = index / sizeof(uint8);
+    uint32 bit_offset = index % sizeof(uint8);
+
+    bh_assert(byte_offset < current_csp->local_use_mask_size);
+    bh_assert(current_csp->local_use_mask);
+
+    current_csp->local_use_mask[byte_offset] |= (1 << bit_offset);
+}
+
+static bool
+wasm_loader_get_local_status(WASMLoaderContext *ctx, uint32 index)
+{
+    BranchBlock *current_csp = ctx->frame_csp - 1;
+    uint32 byte_offset = index / sizeof(uint8);
+    uint32 bit_offset = index % sizeof(uint8);
+
+    bh_assert(byte_offset < current_csp->local_use_mask_size);
+    bh_assert(current_csp->local_use_mask);
+
+    return (current_csp->local_use_mask[byte_offset] & (1 << bit_offset))
+               ? true
+               : false;
+}
+#endif /* end of WASM_ENABLE_GC != 0 */
+
 static void
 wasm_loader_ctx_destroy(WASMLoaderContext *ctx)
 {
@@ -6556,6 +6661,9 @@ wasm_loader_ctx_destroy(WASMLoaderContext *ctx)
         if (ctx->frame_csp_bottom) {
 #if WASM_ENABLE_FAST_INTERP != 0
             free_all_label_patch_lists(ctx->frame_csp_bottom, ctx->csp_num);
+#endif
+#if WASM_ENABLE_GC != 0
+            wasm_loader_clean_all_local_use_masks(ctx);
 #endif
             wasm_runtime_free(ctx->frame_csp_bottom);
         }
@@ -8284,19 +8392,40 @@ fail:
             goto fail;                                               \
     } while (0)
 
+#if WASM_ENABLE_GC == 0
+#define PUSH_CSP(label_type, block_type, _start_addr)                       \
+    do {                                                                    \
+        if (!wasm_loader_push_frame_csp(loader_ctx, label_type, block_type, \
+                                        _start_addr, error_buf,             \
+                                        error_buf_size))                    \
+            goto fail;                                                      \
+    } while (0)
+
+#define POP_CSP()                                                              \
+    do {                                                                       \
+        if (!wasm_loader_pop_frame_csp(loader_ctx, error_buf, error_buf_size)) \
+            goto fail;                                                         \
+    } while (0)
+#else
 #define PUSH_CSP(label_type, block_type, _start_addr)                       \
     do {                                                                    \
         if (!wasm_loader_push_frame_csp(loader_ctx, label_type, block_type, \
                                         _start_addr, error_buf,             \
                                         error_buf_size))                    \
             goto fail;                                                      \
+        if (!wasm_loader_init_local_use_masks(loader_ctx, local_count,      \
+                                              error_buf, error_buf_size)) { \
+            goto fail;                                                      \
+        }                                                                   \
     } while (0)
 
 #define POP_CSP()                                                              \
     do {                                                                       \
+        wasm_loader_destroy_curr_local_use_masks(loader_ctx);                  \
         if (!wasm_loader_pop_frame_csp(loader_ctx, error_buf, error_buf_size)) \
             goto fail;                                                         \
     } while (0)
+#endif /* end of WASM_ENABLE_GC == 0 */
 
 #if WASM_ENABLE_GC == 0
 #define GET_LOCAL_REFTYPE() (void)0
@@ -10537,10 +10666,12 @@ re_scan:
 #if WASM_ENABLE_GC != 0
                 /* Cannot get a non-nullable and unset local */
                 if (local_idx >= param_count
-                    && wasm_is_reftype_htref_non_nullable(local_type)) {
+                    && wasm_is_reftype_htref_non_nullable(local_type)
+                    && !wasm_loader_get_local_status(loader_ctx,
+                                                     local_idx - param_count)) {
                     set_error_buf(error_buf, error_buf_size,
                                   "uninitialized local");
-                    return false;
+                    goto fail;
                 }
 #endif
 
@@ -10643,6 +10774,12 @@ re_scan:
                 }
 #endif
 #endif /* end of WASM_ENABLE_FAST_INTERP != 0 */
+
+#if WASM_ENABLE_GC != 0
+                if (local_idx >= param_count) {
+                    wasm_loader_mask_local(loader_ctx, local_idx - param_count);
+                }
+#endif
                 break;
             }
 
@@ -10711,6 +10848,12 @@ re_scan:
                 }
 #endif
 #endif /* end of WASM_ENABLE_FAST_INTERP != 0 */
+
+#if WASM_ENABLE_GC != 0
+                if (local_idx >= param_count) {
+                    wasm_loader_mask_local(loader_ctx, local_idx - param_count);
+                }
+#endif
                 break;
             }
 
@@ -11543,8 +11686,17 @@ re_scan:
 
                             if (opcode1 == WASM_OP_ARRAY_NEW_CANON_FIXED) {
                                 uint32 N = u32;
-                                for (i = 0; i < N; i++)
+                                for (i = 0; i < N; i++) {
+                                    if (wasm_is_type_multi_byte_type(
+                                            elem_type)) {
+                                        bh_memcpy_s(
+                                            &wasm_ref_type, sizeof(WASMRefType),
+                                            array_type->elem_ref_type,
+                                            wasm_reftype_struct_size(
+                                                array_type->elem_ref_type));
+                                    }
                                     POP_REF(elem_type);
+                                }
                             }
                             else
                                 POP_REF(elem_type);

+ 1 - 20
tests/wamr-test-suites/spec-test-script/gc_ignore_cases.patch

@@ -320,26 +320,7 @@ index 6a8ba1d0..e3059235 100644
  (assert_return (invoke $Ms "get memory[0]") (i32.const 104))  ;; 'h'
  (assert_return (invoke $Ms "get table[0]") (i32.const 0xdead))
 +;)
-diff --git a/test/core/local_get.wast b/test/core/local_get.wast
-index b56aeec3..03465714 100644
---- a/test/core/local_get.wast
-+++ b/test/core/local_get.wast
-@@ -227,6 +227,7 @@
- 
- ;; Uninitialized undefaulted locals
- 
-+(; ;; TODO
- (module
-   (func (export "get-after-set") (param $p (ref extern)) (result (ref extern))
-     (local $x (ref extern))
-@@ -248,6 +249,7 @@
- (assert_return (invoke "get-after-set" (ref.extern 1)) (ref.extern 1))
- (assert_return (invoke "get-after-tee" (ref.extern 2)) (ref.extern 2))
- (assert_return (invoke "get-in-block-after-set" (ref.extern 3)) (ref.extern 3))
-+;)
- 
- (assert_invalid
-   (module (func $uninit (local $x (ref extern)) (drop (local.get $x))))
+
 diff --git a/test/core/ref.wast b/test/core/ref.wast
 index aef1b392..b86db373 100644
 --- a/test/core/ref.wast