Skip to content

Commit f2e4697

Browse files
committed
fix: refactor AOT fuzz testing by consolidating common functions and improving argument handling
1 parent aa09585 commit f2e4697

9 files changed

Lines changed: 268 additions & 338 deletions

File tree

build-scripts/config_common.cmake

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ if (NOT WAMR_BUILD_SANITIZER STREQUAL "")
196196
message(FATAL_ERROR "Unsupported sanitizers: ${INVALID_SANITIZERS}")
197197
endif()
198198
# common flags for all sanitizers
199-
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fno-omit-frame-pointer -fno-sanitize-recover=all -fno-sanitize=alignment")
199+
# clang: warning: the object size sanitizer has no effect at -O0, but is explicitly enabled ... [-Winvalid-command-line-argument]
200+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O1 -fno-omit-frame-pointer -fno-sanitize-recover=all -fno-sanitize=alignment")
200201
if(CMAKE_C_COMPILER_ID MATCHES ".*Clang")
201202
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=unsigned-integer-overflow")
202203
endif()

core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ fd_table_attach(struct fd_table *ft, __wasi_fd_t fd, struct fd_object *fo,
430430
__wasi_rights_t rights_base, __wasi_rights_t rights_inheriting)
431431
REQUIRES_EXCLUSIVE(ft->lock) CONSUMES(fo->refcount)
432432
{
433-
bh_assert(ft->size > fd && "File descriptor table too small");
433+
bh_assert(ft->size > (size_t)fd && "File descriptor table too small");
434434
struct fd_entry *fe = &ft->entries[fd];
435435
bh_assert(fe->object == NULL
436436
&& "Attempted to overwrite an existing descriptor");

tests/fuzz/wasm-mutator-fuzz/aot-compiler/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,6 @@ target_link_directories(aotclib PUBLIC ${LLVM_LIBRARY_DIR})
6161

6262
target_link_libraries(aotclib PUBLIC ${REQUIRED_LLVM_LIBS})
6363

64-
add_executable(aot_compiler_fuzz aot_compiler_fuzz.cc)
64+
add_executable(aot_compiler_fuzz aot_compiler_fuzz.cc ../common/fuzzer_common.cc)
65+
target_include_directories(aot_compiler_fuzz PRIVATE ../common)
6566
target_link_libraries(aot_compiler_fuzz PRIVATE stdc++ aotclib)

tests/fuzz/wasm-mutator-fuzz/aot-compiler/aot_compiler_fuzz.cc

Lines changed: 16 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "aot_export.h"
1212
#include "wasm_export.h"
1313
#include "bh_read_file.h"
14+
#include "../common/fuzzer_common.h"
1415

1516
static void
1617
handle_aot_recent_error(const char *tag)
@@ -23,155 +24,13 @@ handle_aot_recent_error(const char *tag)
2324
std::cout << tag << " " << error << std::endl;
2425
}
2526

26-
static bool
27-
is_supported_val_kind(wasm_valkind_t kind)
28-
{
29-
return kind == WASM_I32 || kind == WASM_I64 || kind == WASM_F32
30-
|| kind == WASM_F64 || kind == WASM_EXTERNREF
31-
|| kind == WASM_FUNCREF;
32-
}
33-
34-
static wasm_val_t
35-
pre_defined_val(wasm_valkind_t kind)
36-
{
37-
if (kind == WASM_I32) {
38-
return wasm_val_t{ .kind = WASM_I32, .of = { .i32 = 2025 } };
39-
}
40-
else if (kind == WASM_I64) {
41-
return wasm_val_t{ .kind = WASM_I64, .of = { .i64 = 168 } };
42-
}
43-
else if (kind == WASM_F32) {
44-
return wasm_val_t{ .kind = WASM_F32, .of = { .f32 = 3.14159f } };
45-
}
46-
else if (kind == WASM_F64) {
47-
return wasm_val_t{ .kind = WASM_F64, .of = { .f64 = 2.71828 } };
48-
}
49-
else if (kind == WASM_EXTERNREF) {
50-
return wasm_val_t{ .kind = WASM_EXTERNREF,
51-
.of = { .foreign = 0xabcddead } };
52-
}
53-
// because aft is_supported_val_kind() check, so we can safely return as
54-
// WASM_FUNCREF
55-
else {
56-
return wasm_val_t{ .kind = WASM_FUNCREF, .of = { .ref = nullptr } };
57-
}
58-
}
59-
void
60-
print_execution_args(const wasm_export_t &export_type,
61-
const std::vector<wasm_val_t> &args, unsigned param_count)
62-
{
63-
std::cout << "[EXECUTION] " << export_type.name << "(";
64-
for (unsigned p_i = 0; p_i < param_count; p_i++) {
65-
if (p_i != 0) {
66-
std::cout << ", ";
67-
}
68-
69-
switch (args[p_i].kind) {
70-
case WASM_I32:
71-
std::cout << "i32:" << args[p_i].of.i32;
72-
break;
73-
case WASM_I64:
74-
std::cout << "i64:" << args[p_i].of.i64;
75-
break;
76-
case WASM_F32:
77-
std::cout << "f32:" << args[p_i].of.f32;
78-
break;
79-
case WASM_F64:
80-
std::cout << "f64:" << args[p_i].of.f64;
81-
break;
82-
case WASM_EXTERNREF:
83-
std::cout << "externref:" << args[p_i].of.foreign;
84-
break;
85-
default:
86-
// because aft is_supported_val_kind() check, so we can safely
87-
// return as WASM_FUNCREF
88-
std::cout << "funcref:" << args[p_i].of.ref;
89-
break;
90-
}
91-
}
92-
std::cout << ")" << std::endl;
93-
}
94-
95-
static bool
96-
execute_export_functions(wasm_module_t module, wasm_module_inst_t inst)
97-
{
98-
int32_t export_count = wasm_runtime_get_export_count(module);
99-
100-
for (int e_i = 0; e_i < export_count; e_i++) {
101-
wasm_export_t export_type = { 0 };
102-
wasm_runtime_get_export_type(module, e_i, &export_type);
103-
104-
if (export_type.kind != WASM_IMPORT_EXPORT_KIND_FUNC) {
105-
continue;
106-
}
107-
108-
wasm_function_inst_t func =
109-
wasm_runtime_lookup_function(inst, export_type.name);
110-
if (!func) {
111-
std::cout << "Failed to lookup function: " << export_type.name
112-
<< std::endl;
113-
continue;
114-
}
115-
116-
wasm_func_type_t func_type = export_type.u.func_type;
117-
uint32_t param_count = wasm_func_type_get_param_count(func_type);
118-
119-
/* build arguments */
120-
std::vector<wasm_val_t> args;
121-
for (unsigned p_i = 0; p_i < param_count; p_i++) {
122-
wasm_valkind_t param_type =
123-
wasm_func_type_get_param_valkind(func_type, p_i);
124-
125-
if (!is_supported_val_kind(param_type)) {
126-
std::cout
127-
<< "Bypass execution because of unsupported value kind: "
128-
<< param_type << std::endl;
129-
return true;
130-
}
131-
132-
wasm_val_t arg = pre_defined_val(param_type);
133-
args.push_back(arg);
134-
}
135-
136-
/* build results storage */
137-
uint32_t result_count = wasm_func_type_get_result_count(func_type);
138-
std::vector<wasm_val_t> results = std::vector<wasm_val_t>(result_count);
139-
140-
print_execution_args(export_type, args, param_count);
141-
142-
/* execute the function */
143-
wasm_exec_env_t exec_env = wasm_runtime_get_exec_env_singleton(inst);
144-
if (!exec_env) {
145-
std::cout << "Failed to get exec env" << std::endl;
146-
return false;
147-
}
148-
149-
bool ret =
150-
wasm_runtime_call_wasm_a(exec_env, func, result_count,
151-
results.data(), param_count, args.data());
152-
if (!ret) {
153-
const char *exception = wasm_runtime_get_exception(inst);
154-
if (!exception) {
155-
std::cout << "[EXECUTION] " << export_type.name
156-
<< "() failed. No exception info." << std::endl;
157-
}
158-
else {
159-
std::cout << "[EXECUTION] " << export_type.name << "() failed. "
160-
<< exception << std::endl;
161-
}
162-
}
163-
164-
wasm_runtime_clear_exception(inst);
165-
}
166-
167-
return true;
168-
}
169-
17027
extern "C" int
17128
LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
17229
{
30+
char kTargetArch[] = "x86_64";
31+
char kTargetAbi[] = "gnu";
17332
wasm_module_t module = NULL;
174-
char error_buf[128] = { 0 };
33+
char error_buf[ERROR_BUF_SIZE] = { 0 };
17534
AOTCompOption option = { 0 };
17635
aot_comp_data_t comp_data = NULL;
17736
aot_comp_context_t comp_ctx = NULL;
@@ -180,26 +39,27 @@ LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
18039
wasm_module_t aot_module = NULL;
18140
wasm_module_inst_t inst = NULL;
18241

183-
/* libfuzzer don't allow to modify the given Data, so make a copy here */
184-
std::vector<uint8_t> myData(Data, Data + Size);
185-
42+
/* libfuzzer don't allow to modify the given Data, but get_package_type and
43+
* wasm_runtime_load only read the data, so we can safely use const_cast */
18644
if (Size >= 4
187-
&& get_package_type(myData.data(), Size) != Wasm_Module_Bytecode) {
45+
&& get_package_type(const_cast<uint8_t *>(Data), Size)
46+
!= Wasm_Module_Bytecode) {
18847
printf("Invalid wasm file: magic header not detected\n");
18948
return 0;
19049
}
19150

19251
wasm_runtime_init();
19352

194-
module = wasm_runtime_load((uint8_t *)myData.data(), Size, error_buf, 120);
53+
module = wasm_runtime_load(const_cast<uint8_t *>(Data), Size, error_buf,
54+
MAX_ERROR_BUF_SIZE);
19555
if (!module) {
19656
std::cout << "[LOADING] " << error_buf << std::endl;
19757
goto DESTROY_RUNTIME;
19858
}
19959

20060
// TODO: target_arch and other fields
201-
option.target_arch = "x86_64";
202-
option.target_abi = "gnu";
61+
option.target_arch = kTargetArch;
62+
option.target_abi = kTargetAbi;
20363
option.enable_bulk_memory = true;
20464
option.enable_thread_mgr = true;
20565
option.enable_tail_call = true;
@@ -232,14 +92,15 @@ LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
23292
goto DESTROY_COMP_CTX;
23393
}
23494

235-
aot_module =
236-
wasm_runtime_load(aot_file_buf, aot_file_size, error_buf, 128);
95+
aot_module = wasm_runtime_load(aot_file_buf, aot_file_size, error_buf,
96+
ERROR_BUF_SIZE);
23797
if (!aot_module) {
23898
std::cout << "[LOADING AOT MODULE] " << error_buf << std::endl;
23999
goto RELEASE_AOT_FILE_BUF;
240100
}
241101

242-
inst = wasm_runtime_instantiate(aot_module, 1024*8, 0, error_buf, 128);
102+
inst = wasm_runtime_instantiate(aot_module, 1024 * 8, 0, error_buf,
103+
ERROR_BUF_SIZE);
243104
if (!inst) {
244105
std::cout << "[INSTANTIATING AOT MODULE] " << error_buf << std::endl;
245106
goto UNLOAD_AOT_MODULE;
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright (C) 2025 Intel Corporation. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include "fuzzer_common.h"
5+
#include <iostream>
6+
#include <string.h>
7+
8+
void
9+
print_execution_args(const wasm_export_t &export_type,
10+
const std::vector<wasm_val_t> &args, unsigned param_count)
11+
{
12+
std::cout << "[EXECUTION] " << export_type.name << "(";
13+
for (unsigned p_i = 0; p_i < param_count; p_i++) {
14+
if (p_i != 0) {
15+
std::cout << ", ";
16+
}
17+
18+
switch (args[p_i].kind) {
19+
case WASM_I32:
20+
std::cout << "i32:" << args[p_i].of.i32;
21+
break;
22+
case WASM_I64:
23+
std::cout << "i64:" << args[p_i].of.i64;
24+
break;
25+
case WASM_F32:
26+
std::cout << "f32:" << args[p_i].of.f32;
27+
break;
28+
case WASM_F64:
29+
std::cout << "f64:" << args[p_i].of.f64;
30+
break;
31+
case WASM_EXTERNREF:
32+
std::cout << "externref:" << args[p_i].of.foreign;
33+
break;
34+
default:
35+
// because aft is_supported_val_kind() check, so we can safely
36+
// return as WASM_FUNCREF
37+
std::cout << "funcref:" << args[p_i].of.ref;
38+
break;
39+
}
40+
}
41+
std::cout << ")" << std::endl;
42+
}
43+
44+
bool
45+
execute_export_functions(wasm_module_t module, wasm_module_inst_t inst)
46+
{
47+
int32_t export_count = wasm_runtime_get_export_count(module);
48+
49+
for (int e_i = 0; e_i < export_count; e_i++) {
50+
wasm_export_t export_type;
51+
52+
memset(&export_type, 0, sizeof(export_type));
53+
wasm_runtime_get_export_type(module, e_i, &export_type);
54+
55+
if (export_type.kind != WASM_IMPORT_EXPORT_KIND_FUNC) {
56+
continue;
57+
}
58+
59+
wasm_function_inst_t func =
60+
wasm_runtime_lookup_function(inst, export_type.name);
61+
if (!func) {
62+
std::cout << "Failed to lookup function: " << export_type.name
63+
<< std::endl;
64+
continue;
65+
}
66+
67+
wasm_func_type_t func_type = export_type.u.func_type;
68+
uint32_t param_count = wasm_func_type_get_param_count(func_type);
69+
70+
/* build arguments with capacity reservation */
71+
std::vector<wasm_val_t> args;
72+
args.reserve(param_count); // Optimization: prevent reallocations
73+
for (unsigned p_i = 0; p_i < param_count; p_i++) {
74+
wasm_valkind_t param_type =
75+
wasm_func_type_get_param_valkind(func_type, p_i);
76+
77+
if (!is_supported_val_kind(param_type)) {
78+
std::cout
79+
<< "Bypass execution because of unsupported value kind: "
80+
<< param_type << std::endl;
81+
return true;
82+
}
83+
84+
wasm_val_t arg = pre_defined_val(param_type);
85+
args.push_back(arg);
86+
}
87+
88+
/* build results storage */
89+
uint32_t result_count = wasm_func_type_get_result_count(func_type);
90+
std::vector<wasm_val_t> results(
91+
result_count); // Optimization: direct initialization
92+
93+
print_execution_args(export_type, args, param_count);
94+
95+
/* execute the function */
96+
wasm_exec_env_t exec_env = wasm_runtime_get_exec_env_singleton(inst);
97+
if (!exec_env) {
98+
std::cout << "Failed to get exec env" << std::endl;
99+
return false;
100+
}
101+
102+
bool ret =
103+
wasm_runtime_call_wasm_a(exec_env, func, result_count,
104+
results.data(), param_count, args.data());
105+
if (!ret) {
106+
const char *exception = wasm_runtime_get_exception(inst);
107+
if (!exception) {
108+
std::cout << "[EXECUTION] " << export_type.name
109+
<< "() failed. No exception info." << std::endl;
110+
}
111+
else {
112+
std::cout << "[EXECUTION] " << export_type.name << "() failed. "
113+
<< exception << std::endl;
114+
}
115+
}
116+
117+
wasm_runtime_clear_exception(inst);
118+
}
119+
120+
return true;
121+
}
122+
123+
void
124+
report_fuzzer_error(FuzzerErrorPhase phase, const char *message)
125+
{
126+
const char *phase_name = "";
127+
switch (phase) {
128+
case FuzzerErrorPhase::LOADING:
129+
phase_name = "LOADING";
130+
break;
131+
case FuzzerErrorPhase::INSTANTIATING:
132+
phase_name = "INSTANTIATING";
133+
break;
134+
case FuzzerErrorPhase::COMPILING:
135+
phase_name = "COMPILING";
136+
break;
137+
case FuzzerErrorPhase::EXECUTION:
138+
phase_name = "EXECUTION";
139+
break;
140+
case FuzzerErrorPhase::CLEANUP:
141+
phase_name = "CLEANUP";
142+
break;
143+
}
144+
std::cout << "[" << phase_name << "] " << message << std::endl;
145+
}

0 commit comments

Comments
 (0)