Переглянути джерело

Fix quadratic runtime for duplicate export name detection (#3861)

Previously, the loader would check the name of a new export against all
existing exports, leading to a quadratic running time.

This change makes the loader parse the entire export section. The
exports are then sorted by name, then adjacent exports are checked for
uniqueness.
James Ring 1 рік тому
батько
коміт
3ad95303d6
2 змінених файлів з 99 додано та 24 видалено
  1. 50 11
      core/iwasm/aot/aot_loader.c
  2. 49 13
      core/iwasm/interpreter/wasm_loader.c

+ 50 - 11
core/iwasm/aot/aot_loader.c

@@ -4,9 +4,8 @@
  */
 
 #include "aot_runtime.h"
-#include "bh_common.h"
-#include "bh_log.h"
 #include "aot_reloc.h"
+#include "bh_platform.h"
 #include "../common/wasm_runtime_common.h"
 #include "../common/wasm_native.h"
 #include "../common/wasm_loader_common.h"
@@ -2753,6 +2752,48 @@ destroy_exports(AOTExport *exports)
     wasm_runtime_free(exports);
 }
 
+static int
+cmp_export_name(const void *a, const void *b)
+{
+    return strcmp(*(char **)a, *(char **)b);
+}
+
+static bool
+check_duplicate_exports(AOTModule *module, char *error_buf,
+                        uint32 error_buf_size)
+{
+    uint32 i;
+    bool result = false;
+    char *names_buf[32], **names = names_buf;
+    if (module->export_count > 32) {
+        names = loader_malloc(module->export_count * sizeof(char *), error_buf,
+                              error_buf_size);
+        if (!names) {
+            return result;
+        }
+    }
+
+    for (i = 0; i < module->export_count; i++) {
+        names[i] = module->exports[i].name;
+    }
+
+    qsort(names, module->export_count, sizeof(char *), cmp_export_name);
+
+    for (i = 1; i < module->export_count; i++) {
+        if (!strcmp(names[i], names[i - 1])) {
+            set_error_buf(error_buf, error_buf_size, "duplicate export name");
+            goto cleanup;
+        }
+    }
+
+    result = true;
+cleanup:
+    if (module->export_count > 32) {
+        wasm_runtime_free(names);
+    }
+    return result;
+}
+
 static bool
 load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
              bool is_load_from_file_buf, char *error_buf, uint32 error_buf_size)
@@ -2760,7 +2801,7 @@ load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
     const uint8 *buf = *p_buf;
     AOTExport *exports;
     uint64 size;
-    uint32 i, j;
+    uint32 i;
 
     /* Allocate memory */
     size = sizeof(AOTExport) * (uint64)module->export_count;
@@ -2775,14 +2816,6 @@ load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
         read_uint8(buf, buf_end, exports[i].kind);
         read_string(buf, buf_end, exports[i].name);
 
-        for (j = 0; j < i; j++) {
-            if (!strcmp(exports[i].name, exports[j].name)) {
-                set_error_buf(error_buf, error_buf_size,
-                              "duplicate export name");
-                return false;
-            }
-        }
-
         /* Check export kind and index */
         switch (exports[i].kind) {
             case EXPORT_KIND_FUNC:
@@ -2830,6 +2863,12 @@ load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
         }
     }
 
+    if (module->export_count > 0) {
+        if (!check_duplicate_exports(module, error_buf, error_buf_size)) {
+            return false;
+        }
+    }
+
     *p_buf = buf;
     return true;
 fail:

+ 49 - 13
core/iwasm/interpreter/wasm_loader.c

@@ -4,8 +4,7 @@
  */
 
 #include "wasm_loader.h"
-#include "bh_common.h"
-#include "bh_log.h"
+#include "bh_platform.h"
 #include "wasm.h"
 #include "wasm_opcode.h"
 #include "wasm_runtime.h"
@@ -3184,6 +3183,12 @@ fail:
     return false;
 }
 
+static int
+cmp_export_name(const void *a, const void *b)
+{
+    return strcmp(*(char **)a, *(char **)b);
+}
+
 static bool
 load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                     bool is_load_from_file_buf, bool no_resolve,
@@ -4054,17 +4059,53 @@ fail:
     return false;
 }
 
+static bool
+check_duplicate_exports(WASMModule *module, char *error_buf,
+                        uint32 error_buf_size)
+{
+    uint32 i;
+    bool result = false;
+    char *names_buf[32], **names = names_buf;
+
+    if (module->export_count > 32) {
+        names = loader_malloc(module->export_count * sizeof(char *), error_buf,
+                              error_buf_size);
+        if (!names) {
+            return result;
+        }
+    }
+
+    for (i = 0; i < module->export_count; i++) {
+        names[i] = module->exports[i].name;
+    }
+
+    qsort(names, module->export_count, sizeof(char *), cmp_export_name);
+
+    for (i = 1; i < module->export_count; i++) {
+        if (!strcmp(names[i], names[i - 1])) {
+            set_error_buf(error_buf, error_buf_size, "duplicate export name");
+            goto cleanup;
+        }
+    }
+
+    result = true;
+cleanup:
+    if (module->export_count > 32) {
+        wasm_runtime_free(names);
+    }
+    return result;
+}
+
 static bool
 load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                     bool is_load_from_file_buf, char *error_buf,
                     uint32 error_buf_size)
 {
     const uint8 *p = buf, *p_end = buf_end;
-    uint32 export_count, i, j, index;
+    uint32 export_count, i, index;
     uint64 total_size;
     uint32 str_len;
     WASMExport *export;
-    const char *name;
 
     read_leb_uint32(p, p_end, export_count);
 
@@ -4090,15 +4131,6 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
             read_leb_uint32(p, p_end, str_len);
             CHECK_BUF(p, p_end, str_len);
 
-            for (j = 0; j < i; j++) {
-                name = module->exports[j].name;
-                if (strlen(name) == str_len && memcmp(name, p, str_len) == 0) {
-                    set_error_buf(error_buf, error_buf_size,
-                                  "duplicate export name");
-                    return false;
-                }
-            }
-
             if (!(export->name = wasm_const_str_list_insert(
                       p, str_len, module, is_load_from_file_buf, error_buf,
                       error_buf_size))) {
@@ -4171,6 +4203,10 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                     return false;
             }
         }
+
+        if (!check_duplicate_exports(module, error_buf, error_buf_size)) {
+            return false;
+        }
     }
 
     if (p != p_end) {