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.
This commit is contained in:
James Ring 2024-10-20 20:02:30 -07:00 committed by GitHub
parent 87588caa7f
commit 3ad95303d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 99 additions and 24 deletions

View File

@ -4,9 +4,8 @@
*/ */
#include "aot_runtime.h" #include "aot_runtime.h"
#include "bh_common.h"
#include "bh_log.h"
#include "aot_reloc.h" #include "aot_reloc.h"
#include "bh_platform.h"
#include "../common/wasm_runtime_common.h" #include "../common/wasm_runtime_common.h"
#include "../common/wasm_native.h" #include "../common/wasm_native.h"
#include "../common/wasm_loader_common.h" #include "../common/wasm_loader_common.h"
@ -2753,6 +2752,48 @@ destroy_exports(AOTExport *exports)
wasm_runtime_free(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 static bool
load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, 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) 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; const uint8 *buf = *p_buf;
AOTExport *exports; AOTExport *exports;
uint64 size; uint64 size;
uint32 i, j; uint32 i;
/* Allocate memory */ /* Allocate memory */
size = sizeof(AOTExport) * (uint64)module->export_count; 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_uint8(buf, buf_end, exports[i].kind);
read_string(buf, buf_end, exports[i].name); 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 */ /* Check export kind and index */
switch (exports[i].kind) { switch (exports[i].kind) {
case EXPORT_KIND_FUNC: 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; *p_buf = buf;
return true; return true;
fail: fail:

View File

@ -4,8 +4,7 @@
*/ */
#include "wasm_loader.h" #include "wasm_loader.h"
#include "bh_common.h" #include "bh_platform.h"
#include "bh_log.h"
#include "wasm.h" #include "wasm.h"
#include "wasm_opcode.h" #include "wasm_opcode.h"
#include "wasm_runtime.h" #include "wasm_runtime.h"
@ -3184,6 +3183,12 @@ fail:
return false; return false;
} }
static int
cmp_export_name(const void *a, const void *b)
{
return strcmp(*(char **)a, *(char **)b);
}
static bool static bool
load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
bool is_load_from_file_buf, bool no_resolve, bool is_load_from_file_buf, bool no_resolve,
@ -4054,17 +4059,53 @@ fail:
return false; 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 static bool
load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
bool is_load_from_file_buf, char *error_buf, bool is_load_from_file_buf, char *error_buf,
uint32 error_buf_size) uint32 error_buf_size)
{ {
const uint8 *p = buf, *p_end = buf_end; const uint8 *p = buf, *p_end = buf_end;
uint32 export_count, i, j, index; uint32 export_count, i, index;
uint64 total_size; uint64 total_size;
uint32 str_len; uint32 str_len;
WASMExport *export; WASMExport *export;
const char *name;
read_leb_uint32(p, p_end, export_count); 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); read_leb_uint32(p, p_end, str_len);
CHECK_BUF(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( if (!(export->name = wasm_const_str_list_insert(
p, str_len, module, is_load_from_file_buf, error_buf, p, str_len, module, is_load_from_file_buf, error_buf,
error_buf_size))) { error_buf_size))) {
@ -4171,6 +4203,10 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
return false; return false;
} }
} }
if (!check_duplicate_exports(module, error_buf, error_buf_size)) {
return false;
}
} }
if (p != p_end) { if (p != p_end) {