From 4e0bef519805ee7de2e0e3ddc50643e5bbc2da9b Mon Sep 17 00:00:00 2001 From: "liang.he@intel.com" Date: Sun, 15 Dec 2024 12:23:39 +0000 Subject: [PATCH 1/6] Enhance wasm mutator fuzz tests by adding export function execution and random value generation --- tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh | 1 + .../wasm-mutator-fuzz/wasm_mutator_fuzz.cc | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh b/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh index 02ac831742..227a043b00 100755 --- a/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh +++ b/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh @@ -42,6 +42,7 @@ function try_generate_wasm() } WASM_SHAPE=" --allow-invalid-funcs true \ +--export-everything true \ --generate-custom-sections true \ --min-funcs 5 \ --max-instructions 1024 \ diff --git a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc index 2d5a667039..4f117311b7 100644 --- a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc +++ b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc @@ -10,9 +10,105 @@ #include #include #include +#include using namespace std; +/* use std generation */ +static wasm_val_t +random_gen_val(wasm_valkind_t kind) +{ + static std::random_device rd; + static std::mt19937 gen(rd()); + if (kind == WASM_I32) { + std::uniform_int_distribution dis; + return wasm_val_t{ .kind = WASM_I32, .of = { .i32 = dis(gen) } }; + } + else if (kind == WASM_I64) { + std::uniform_int_distribution dis; + return wasm_val_t{ .kind = WASM_I64, .of = { .i64 = dis(gen) } }; + } + else if (kind == WASM_F32) { + std::uniform_real_distribution dis; + return wasm_val_t{ .kind = WASM_F32, .of = { .f32 = dis(gen) } }; + } + else if (kind == WASM_F64) { + std::uniform_real_distribution dis; + return wasm_val_t{ .kind = WASM_F64, .of = { .f64 = dis(gen) } }; + } + else if (kind == WASM_EXTERNREF) { + // TODO: + return wasm_val_t{ .kind = WASM_EXTERNREF, .of = { .foreign = 0 } }; + } + else if (kind == WASM_FUNCREF) { + // TODO: + return wasm_val_t{ .kind = WASM_FUNCREF, .of = { .ref = nullptr } }; + } + else { + assert(0); + } +} + +static bool +execute_export_functions(wasm_module_t module, wasm_module_inst_t inst) +{ + int32_t export_count = wasm_runtime_get_export_count(module); + + for (int e_i = 0; e_i < export_count; e_i++) { + wasm_export_t export_type = { 0 }; + wasm_runtime_get_export_type(module, e_i, &export_type); + + if (export_type.kind != WASM_IMPORT_EXPORT_KIND_FUNC) { + continue; + } + + wasm_function_inst_t func = + wasm_runtime_lookup_function(inst, export_type.name); + if (!func) { + std::cout << "Failed to lookup function: " << export_type.name + << std::endl; + continue; + } + + wasm_func_type_t func_type = export_type.u.func_type; + uint32_t param_count = wasm_func_type_get_param_count(func_type); + + /* build arguments */ + std::vector args; + for (unsigned p_i = 0; p_i < param_count; p_i++) { + wasm_valkind_t param_type = + wasm_func_type_get_param_valkind(func_type, p_i); + wasm_val_t arg = random_gen_val(param_type); + args.push_back(arg); + } + + /* build results storage */ + uint32_t result_count = wasm_func_type_get_result_count(func_type); + std::vector results = std::vector(result_count); + + /* execute the function */ + wasm_exec_env_t exec_env = wasm_runtime_get_exec_env_singleton(inst); + bool ret = + wasm_runtime_call_wasm_a(exec_env, func, result_count, + results.data(), param_count, args.data()); + if (!ret) { + const char *exception = wasm_runtime_get_exception(inst); + if (!exception) { + std::cout << "Failed to execute function: " << export_type.name + << ". No exception info." << std::endl; + } + else { + std::cout << "Failed to execute function: " << export_type.name + << ". " << exception << std::endl; + } + } + + wasm_runtime_clear_exception(inst); + } + + return true; +} + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { @@ -43,6 +139,8 @@ LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) return 0; } + execute_export_functions(module, inst); + std::cout << "PASS" << std::endl; wasm_runtime_deinstantiate(inst); From 5ced3ae5965327d67f49a9439b07e2f62b011502 Mon Sep 17 00:00:00 2001 From: "liang.he@intel.com" Date: Mon, 23 Dec 2024 09:16:34 +0000 Subject: [PATCH 2/6] Enhance wasm mutator fuzz tests with improved argument logging --- .../wasm-mutator-fuzz/wasm_mutator_fuzz.cc | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc index 4f117311b7..af7cf07ce0 100644 --- a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc +++ b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc @@ -37,15 +37,16 @@ random_gen_val(wasm_valkind_t kind) return wasm_val_t{ .kind = WASM_F64, .of = { .f64 = dis(gen) } }; } else if (kind == WASM_EXTERNREF) { - // TODO: - return wasm_val_t{ .kind = WASM_EXTERNREF, .of = { .foreign = 0 } }; + std::uniform_int_distribution dis; + return wasm_val_t{ .kind = WASM_EXTERNREF, + .of = { .foreign = dis(gen) } }; } else if (kind == WASM_FUNCREF) { - // TODO: return wasm_val_t{ .kind = WASM_FUNCREF, .of = { .ref = nullptr } }; } + // TODO:v128 else { - assert(0); + assert(0 && "unsupported value kind"); } } @@ -88,18 +89,53 @@ execute_export_functions(wasm_module_t module, wasm_module_inst_t inst) /* execute the function */ wasm_exec_env_t exec_env = wasm_runtime_get_exec_env_singleton(inst); + + { + std::cout << "[EXECUTION] " << export_type.name << "("; + for (unsigned p_i = 0; p_i < param_count; p_i++) { + if (p_i != 0) { + std::cout << ", "; + } + + if (args[p_i].kind == WASM_I32) { + std::cout << "i32:" << args[p_i].of.i32; + } + else if (args[p_i].kind == WASM_I64) { + std::cout << "i64:" << args[p_i].of.i64; + } + else if (args[p_i].kind == WASM_F32) { + std::cout << "f32:" << args[p_i].of.f32; + } + else if (args[p_i].kind == WASM_F64) { + std::cout << "f64:" << args[p_i].of.f64; + } + else if (args[p_i].kind == WASM_EXTERNREF) { + std::cout << "externref:" << args[p_i].of.foreign; + } + else if (args[p_i].kind == WASM_FUNCREF) { + std::cout << "funcref:" << args[p_i].of.ref; + } + // TODO:v128 + else { + assert(0 && "unsupported value kind"); + } + } + + std::cout << ")"; + } + bool ret = wasm_runtime_call_wasm_a(exec_env, func, result_count, results.data(), param_count, args.data()); if (!ret) { const char *exception = wasm_runtime_get_exception(inst); if (!exception) { - std::cout << "Failed to execute function: " << export_type.name - << ". No exception info." << std::endl; + std::cout << "[EXECUTION] " << export_type.name + << "() failed. No exception info." << std::endl; } else { - std::cout << "Failed to execute function: " << export_type.name - << ". " << exception << std::endl; + std::cout << "[EXECUTION] " << export_type.name << "() failed. " + << exception << std::endl; } } @@ -141,8 +177,6 @@ LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) execute_export_functions(module, inst); - std::cout << "PASS" << std::endl; - wasm_runtime_deinstantiate(inst); wasm_runtime_unload(module); wasm_runtime_destroy(); From 7d51962be725789f720ddb57e2881f577e07aa65 Mon Sep 17 00:00:00 2001 From: "liang.he@intel.com" Date: Tue, 24 Dec 2024 03:39:20 +0000 Subject: [PATCH 3/6] Refactor wasm mutator fuzz tests to use simpler random number generation and ensure termination in WASM shape --- tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh | 2 +- .../wasm-mutator-fuzz/wasm_mutator_fuzz.cc | 63 ++++++++++++++----- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh b/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh index 227a043b00..ac0e414c46 100755 --- a/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh +++ b/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh @@ -41,7 +41,7 @@ function try_generate_wasm() printf -- "-- output ${GENERATED_WASM_NAME} in %d retries\n" $try_i } -WASM_SHAPE=" --allow-invalid-funcs true \ +WASM_SHAPE=" --ensure-termination \ --export-everything true \ --generate-custom-sections true \ --min-funcs 5 \ diff --git a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc index af7cf07ce0..42252fcc18 100644 --- a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc +++ b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc @@ -10,36 +10,66 @@ #include #include #include -#include +#include using namespace std; -/* use std generation */ +/* + * there is a unsigned integer overflow in + * /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/random.tcc:416 + * + * use srand() and rand() instead + */ + +static int32_t +generate_random_int32() +{ + return (int32_t)rand(); +} + +static int64_t +generate_random_int64() +{ + return ((int64_t)rand() << 32) | rand(); +} + +static float +generate_random_float() +{ + return (float)rand() / (float)RAND_MAX; +} + +static double +generate_random_double() +{ + return (double)rand() / (double)RAND_MAX; +} + static wasm_val_t random_gen_val(wasm_valkind_t kind) { - static std::random_device rd; - static std::mt19937 gen(rd()); + srand(1024); + if (kind == WASM_I32) { - std::uniform_int_distribution dis; - return wasm_val_t{ .kind = WASM_I32, .of = { .i32 = dis(gen) } }; + return wasm_val_t{ .kind = WASM_I32, + .of = { .i32 = generate_random_int32() } }; } else if (kind == WASM_I64) { - std::uniform_int_distribution dis; - return wasm_val_t{ .kind = WASM_I64, .of = { .i64 = dis(gen) } }; + return wasm_val_t{ .kind = WASM_I64, + .of = { .i64 = generate_random_int64() } }; } else if (kind == WASM_F32) { - std::uniform_real_distribution dis; - return wasm_val_t{ .kind = WASM_F32, .of = { .f32 = dis(gen) } }; + return wasm_val_t{ .kind = WASM_F32, + .of = { .f32 = generate_random_float() } }; } else if (kind == WASM_F64) { - std::uniform_real_distribution dis; - return wasm_val_t{ .kind = WASM_F64, .of = { .f64 = dis(gen) } }; + return wasm_val_t{ .kind = WASM_F64, + .of = { .f64 = generate_random_double() } }; } else if (kind == WASM_EXTERNREF) { - std::uniform_int_distribution dis; return wasm_val_t{ .kind = WASM_EXTERNREF, - .of = { .foreign = dis(gen) } }; + .of = { .foreign = + (uintptr_t)generate_random_int64() } }; } else if (kind == WASM_FUNCREF) { return wasm_val_t{ .kind = WASM_FUNCREF, .of = { .ref = nullptr } }; @@ -124,9 +154,8 @@ execute_export_functions(wasm_module_t module, wasm_module_inst_t inst) std::cout << ")"; } - bool ret = - wasm_runtime_call_wasm_a(exec_env, func, result_count, - results.data(), param_count, args.data()); + bool ret = wasm_runtime_call_wasm_a(exec_env, func, result_count, + &results[0], param_count, &args[0]); if (!ret) { const char *exception = wasm_runtime_get_exception(inst); if (!exception) { From 5f0f8524c5d8716c49d8686b1a13c9bf5ccf9a13 Mon Sep 17 00:00:00 2001 From: "liang.he@intel.com" Date: Tue, 24 Dec 2024 06:36:30 +0000 Subject: [PATCH 4/6] Remove unnecessary inclusion of --- tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc index 42252fcc18..471df8ba89 100644 --- a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc +++ b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc @@ -10,7 +10,6 @@ #include #include #include -#include using namespace std; From bb524182162b1e8ad4aba16a03c46d149f3ae356 Mon Sep 17 00:00:00 2001 From: "liang.he@intel.com" Date: Tue, 24 Dec 2024 06:55:59 +0000 Subject: [PATCH 5/6] use --fuel to limit loop size --- tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh | 1 + tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh b/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh index ac0e414c46..097e5348b4 100755 --- a/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh +++ b/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh @@ -43,6 +43,7 @@ function try_generate_wasm() WASM_SHAPE=" --ensure-termination \ --export-everything true \ +--fuel 7 \ --generate-custom-sections true \ --min-funcs 5 \ --max-instructions 1024 \ diff --git a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc index 471df8ba89..fdbd2f8841 100644 --- a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc +++ b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc @@ -150,7 +150,7 @@ execute_export_functions(wasm_module_t module, wasm_module_inst_t inst) } } - std::cout << ")"; + std::cout << ")" << std::endl; } bool ret = wasm_runtime_call_wasm_a(exec_env, func, result_count, From 59b15811c28e405b93f7babbd620da672ce3946c Mon Sep 17 00:00:00 2001 From: "liang.he@intel.com" Date: Sun, 5 Jan 2025 08:12:30 +0000 Subject: [PATCH 6/6] Refactor wasm interpreter to use predefined values and enhance argument logging in execution --- core/iwasm/interpreter/wasm_interp_fast.c | 7 +- .../wasm-mutator-fuzz/wasm_mutator_fuzz.cc | 143 ++++++++---------- 2 files changed, 67 insertions(+), 83 deletions(-) diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 531468282f..f44644e456 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -1670,7 +1670,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, { uint32 ret_idx; WASMFuncType *func_type; - uint32 off, ret_offset; + int32 off; + uint32 ret_offset; uint8 *ret_types; if (cur_func->is_import_func) func_type = cur_func->u.func_import->func_type; @@ -1682,9 +1683,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, ret_offset = prev_frame->ret_offset; for (ret_idx = 0, - off = sizeof(int16) * (func_type->result_count - 1); + off = (int32)sizeof(int16) * (func_type->result_count - 1); ret_idx < func_type->result_count; - ret_idx++, off -= sizeof(int16)) { + ret_idx++, off -= (int32)sizeof(int16)) { if (ret_types[ret_idx] == VALUE_TYPE_I64 || ret_types[ret_idx] == VALUE_TYPE_F64) { PUT_I64_TO_ADDR(prev_frame->lp + ret_offset, diff --git a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc index fdbd2f8841..4b3d8d942d 100644 --- a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc +++ b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc @@ -13,70 +13,73 @@ using namespace std; -/* - * there is a unsigned integer overflow in - * /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/random.tcc:416 - * - * use srand() and rand() instead - */ - -static int32_t -generate_random_int32() -{ - return (int32_t)rand(); -} - -static int64_t -generate_random_int64() -{ - return ((int64_t)rand() << 32) | rand(); -} - -static float -generate_random_float() -{ - return (float)rand() / (float)RAND_MAX; -} - -static double -generate_random_double() +static bool +is_supported_val_kind(wasm_valkind_t kind) { - return (double)rand() / (double)RAND_MAX; + return kind == WASM_I32 || kind == WASM_I64 || kind == WASM_F32 + || kind == WASM_F64 || kind == WASM_EXTERNREF + || kind == WASM_FUNCREF; } static wasm_val_t -random_gen_val(wasm_valkind_t kind) +pre_defined_val(wasm_valkind_t kind) { - srand(1024); - if (kind == WASM_I32) { - return wasm_val_t{ .kind = WASM_I32, - .of = { .i32 = generate_random_int32() } }; + return wasm_val_t{ .kind = WASM_I32, .of = { .i32 = 2025 } }; } else if (kind == WASM_I64) { - return wasm_val_t{ .kind = WASM_I64, - .of = { .i64 = generate_random_int64() } }; + return wasm_val_t{ .kind = WASM_I64, .of = { .i64 = 168 } }; } else if (kind == WASM_F32) { - return wasm_val_t{ .kind = WASM_F32, - .of = { .f32 = generate_random_float() } }; + return wasm_val_t{ .kind = WASM_F32, .of = { .f32 = 3.14159f } }; } else if (kind == WASM_F64) { - return wasm_val_t{ .kind = WASM_F64, - .of = { .f64 = generate_random_double() } }; + return wasm_val_t{ .kind = WASM_F64, .of = { .f64 = 2.71828 } }; } else if (kind == WASM_EXTERNREF) { return wasm_val_t{ .kind = WASM_EXTERNREF, - .of = { .foreign = - (uintptr_t)generate_random_int64() } }; + .of = { .foreign = 0xabcddead } }; } - else if (kind == WASM_FUNCREF) { + // because aft is_supported_val_kind() check, so we can safely return as + // WASM_FUNCREF + else { return wasm_val_t{ .kind = WASM_FUNCREF, .of = { .ref = nullptr } }; } - // TODO:v128 - else { - assert(0 && "unsupported value kind"); +} +void +print_execution_args(const wasm_export_t &export_type, + const std::vector &args, unsigned param_count) +{ + std::cout << "[EXECUTION] " << export_type.name << "("; + for (unsigned p_i = 0; p_i < param_count; p_i++) { + if (p_i != 0) { + std::cout << ", "; + } + + switch (args[p_i].kind) { + case WASM_I32: + std::cout << "i32:" << args[p_i].of.i32; + break; + case WASM_I64: + std::cout << "i64:" << args[p_i].of.i64; + break; + case WASM_F32: + std::cout << "f32:" << args[p_i].of.f32; + break; + case WASM_F64: + std::cout << "f64:" << args[p_i].of.f64; + break; + case WASM_EXTERNREF: + std::cout << "externref:" << args[p_i].of.foreign; + break; + default: + // because aft is_supported_val_kind() check, so we can safely + // return as WASM_FUNCREF + std::cout << "funcref:" << args[p_i].of.ref; + break; + } } + std::cout << ")" << std::endl; } static bool @@ -108,7 +111,15 @@ execute_export_functions(wasm_module_t module, wasm_module_inst_t inst) for (unsigned p_i = 0; p_i < param_count; p_i++) { wasm_valkind_t param_type = wasm_func_type_get_param_valkind(func_type, p_i); - wasm_val_t arg = random_gen_val(param_type); + + if (!is_supported_val_kind(param_type)) { + std::cout + << "Bypass execution because of unsupported value kind: " + << param_type << std::endl; + return true; + } + + wasm_val_t arg = pre_defined_val(param_type); args.push_back(arg); } @@ -116,45 +127,17 @@ execute_export_functions(wasm_module_t module, wasm_module_inst_t inst) uint32_t result_count = wasm_func_type_get_result_count(func_type); std::vector results = std::vector(result_count); + print_execution_args(export_type, args, param_count); + /* execute the function */ wasm_exec_env_t exec_env = wasm_runtime_get_exec_env_singleton(inst); - - { - std::cout << "[EXECUTION] " << export_type.name << "("; - for (unsigned p_i = 0; p_i < param_count; p_i++) { - if (p_i != 0) { - std::cout << ", "; - } - - if (args[p_i].kind == WASM_I32) { - std::cout << "i32:" << args[p_i].of.i32; - } - else if (args[p_i].kind == WASM_I64) { - std::cout << "i64:" << args[p_i].of.i64; - } - else if (args[p_i].kind == WASM_F32) { - std::cout << "f32:" << args[p_i].of.f32; - } - else if (args[p_i].kind == WASM_F64) { - std::cout << "f64:" << args[p_i].of.f64; - } - else if (args[p_i].kind == WASM_EXTERNREF) { - std::cout << "externref:" << args[p_i].of.foreign; - } - else if (args[p_i].kind == WASM_FUNCREF) { - std::cout << "funcref:" << args[p_i].of.ref; - } - // TODO:v128 - else { - assert(0 && "unsupported value kind"); - } - } - - std::cout << ")" << std::endl; + if (!exec_env) { + std::cout << "Failed to get exec env" << std::endl; + return false; } bool ret = wasm_runtime_call_wasm_a(exec_env, func, result_count, - &results[0], param_count, &args[0]); + results.data(), param_count, args.data()); if (!ret) { const char *exception = wasm_runtime_get_exception(inst); if (!exception) {