Skip to content

Commit 3ad9530

Browse files
authored
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.
1 parent 87588ca commit 3ad9530

File tree

2 files changed

+99
-24
lines changed

2 files changed

+99
-24
lines changed

core/iwasm/aot/aot_loader.c

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
*/
55

66
#include "aot_runtime.h"
7-
#include "bh_common.h"
8-
#include "bh_log.h"
97
#include "aot_reloc.h"
8+
#include "bh_platform.h"
109
#include "../common/wasm_runtime_common.h"
1110
#include "../common/wasm_native.h"
1211
#include "../common/wasm_loader_common.h"
@@ -2753,14 +2752,56 @@ destroy_exports(AOTExport *exports)
27532752
wasm_runtime_free(exports);
27542753
}
27552754

2755+
static int
2756+
cmp_export_name(const void *a, const void *b)
2757+
{
2758+
return strcmp(*(char **)a, *(char **)b);
2759+
}
2760+
2761+
static bool
2762+
check_duplicate_exports(AOTModule *module, char *error_buf,
2763+
uint32 error_buf_size)
2764+
{
2765+
uint32 i;
2766+
bool result = false;
2767+
char *names_buf[32], **names = names_buf;
2768+
if (module->export_count > 32) {
2769+
names = loader_malloc(module->export_count * sizeof(char *), error_buf,
2770+
error_buf_size);
2771+
if (!names) {
2772+
return result;
2773+
}
2774+
}
2775+
2776+
for (i = 0; i < module->export_count; i++) {
2777+
names[i] = module->exports[i].name;
2778+
}
2779+
2780+
qsort(names, module->export_count, sizeof(char *), cmp_export_name);
2781+
2782+
for (i = 1; i < module->export_count; i++) {
2783+
if (!strcmp(names[i], names[i - 1])) {
2784+
set_error_buf(error_buf, error_buf_size, "duplicate export name");
2785+
goto cleanup;
2786+
}
2787+
}
2788+
2789+
result = true;
2790+
cleanup:
2791+
if (module->export_count > 32) {
2792+
wasm_runtime_free(names);
2793+
}
2794+
return result;
2795+
}
2796+
27562797
static bool
27572798
load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
27582799
bool is_load_from_file_buf, char *error_buf, uint32 error_buf_size)
27592800
{
27602801
const uint8 *buf = *p_buf;
27612802
AOTExport *exports;
27622803
uint64 size;
2763-
uint32 i, j;
2804+
uint32 i;
27642805

27652806
/* Allocate memory */
27662807
size = sizeof(AOTExport) * (uint64)module->export_count;
@@ -2775,14 +2816,6 @@ load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
27752816
read_uint8(buf, buf_end, exports[i].kind);
27762817
read_string(buf, buf_end, exports[i].name);
27772818

2778-
for (j = 0; j < i; j++) {
2779-
if (!strcmp(exports[i].name, exports[j].name)) {
2780-
set_error_buf(error_buf, error_buf_size,
2781-
"duplicate export name");
2782-
return false;
2783-
}
2784-
}
2785-
27862819
/* Check export kind and index */
27872820
switch (exports[i].kind) {
27882821
case EXPORT_KIND_FUNC:
@@ -2830,6 +2863,12 @@ load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
28302863
}
28312864
}
28322865

2866+
if (module->export_count > 0) {
2867+
if (!check_duplicate_exports(module, error_buf, error_buf_size)) {
2868+
return false;
2869+
}
2870+
}
2871+
28332872
*p_buf = buf;
28342873
return true;
28352874
fail:

core/iwasm/interpreter/wasm_loader.c

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
*/
55

66
#include "wasm_loader.h"
7-
#include "bh_common.h"
8-
#include "bh_log.h"
7+
#include "bh_platform.h"
98
#include "wasm.h"
109
#include "wasm_opcode.h"
1110
#include "wasm_runtime.h"
@@ -3184,6 +3183,12 @@ load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory,
31843183
return false;
31853184
}
31863185

3186+
static int
3187+
cmp_export_name(const void *a, const void *b)
3188+
{
3189+
return strcmp(*(char **)a, *(char **)b);
3190+
}
3191+
31873192
static bool
31883193
load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
31893194
bool is_load_from_file_buf, bool no_resolve,
@@ -4054,17 +4059,53 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
40544059
return false;
40554060
}
40564061

4062+
static bool
4063+
check_duplicate_exports(WASMModule *module, char *error_buf,
4064+
uint32 error_buf_size)
4065+
{
4066+
uint32 i;
4067+
bool result = false;
4068+
char *names_buf[32], **names = names_buf;
4069+
4070+
if (module->export_count > 32) {
4071+
names = loader_malloc(module->export_count * sizeof(char *), error_buf,
4072+
error_buf_size);
4073+
if (!names) {
4074+
return result;
4075+
}
4076+
}
4077+
4078+
for (i = 0; i < module->export_count; i++) {
4079+
names[i] = module->exports[i].name;
4080+
}
4081+
4082+
qsort(names, module->export_count, sizeof(char *), cmp_export_name);
4083+
4084+
for (i = 1; i < module->export_count; i++) {
4085+
if (!strcmp(names[i], names[i - 1])) {
4086+
set_error_buf(error_buf, error_buf_size, "duplicate export name");
4087+
goto cleanup;
4088+
}
4089+
}
4090+
4091+
result = true;
4092+
cleanup:
4093+
if (module->export_count > 32) {
4094+
wasm_runtime_free(names);
4095+
}
4096+
return result;
4097+
}
4098+
40574099
static bool
40584100
load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
40594101
bool is_load_from_file_buf, char *error_buf,
40604102
uint32 error_buf_size)
40614103
{
40624104
const uint8 *p = buf, *p_end = buf_end;
4063-
uint32 export_count, i, j, index;
4105+
uint32 export_count, i, index;
40644106
uint64 total_size;
40654107
uint32 str_len;
40664108
WASMExport *export;
4067-
const char *name;
40684109

40694110
read_leb_uint32(p, p_end, export_count);
40704111

@@ -4090,15 +4131,6 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
40904131
read_leb_uint32(p, p_end, str_len);
40914132
CHECK_BUF(p, p_end, str_len);
40924133

4093-
for (j = 0; j < i; j++) {
4094-
name = module->exports[j].name;
4095-
if (strlen(name) == str_len && memcmp(name, p, str_len) == 0) {
4096-
set_error_buf(error_buf, error_buf_size,
4097-
"duplicate export name");
4098-
return false;
4099-
}
4100-
}
4101-
41024134
if (!(export->name = wasm_const_str_list_insert(
41034135
p, str_len, module, is_load_from_file_buf, error_buf,
41044136
error_buf_size))) {
@@ -4171,6 +4203,10 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
41714203
return false;
41724204
}
41734205
}
4206+
4207+
if (!check_duplicate_exports(module, error_buf, error_buf_size)) {
4208+
return false;
4209+
}
41744210
}
41754211

41764212
if (p != p_end) {

0 commit comments

Comments
 (0)