From c99ae24fb6798201c70697f3ac6f431758d81b4a Mon Sep 17 00:00:00 2001 From: "liang.he" Date: Thu, 6 Feb 2025 13:15:56 +0800 Subject: [PATCH] [fuzzing] execute every exported function (#3959) - Enhance wasm mutator fuzz tests by adding export function execution and random value generation - Use --fuel to limit loop size - Use predefined values and enhance argument logging in execution --- tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh | 4 +- .../wasm-mutator-fuzz/wasm_mutator_fuzz.cc | 145 +++++++++++++++++- 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh b/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh index 02ac83174..097e5348b 100755 --- a/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh +++ b/tests/fuzz/wasm-mutator-fuzz/smith_wasm.sh @@ -41,7 +41,9 @@ 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 \ +--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 2d5a66703..4b3d8d942 100644 --- a/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc +++ b/tests/fuzz/wasm-mutator-fuzz/wasm_mutator_fuzz.cc @@ -13,6 +13,149 @@ using namespace std; +static bool +is_supported_val_kind(wasm_valkind_t kind) +{ + return kind == WASM_I32 || kind == WASM_I64 || kind == WASM_F32 + || kind == WASM_F64 || kind == WASM_EXTERNREF + || kind == WASM_FUNCREF; +} + +static wasm_val_t +pre_defined_val(wasm_valkind_t kind) +{ + if (kind == WASM_I32) { + return wasm_val_t{ .kind = WASM_I32, .of = { .i32 = 2025 } }; + } + else if (kind == WASM_I64) { + return wasm_val_t{ .kind = WASM_I64, .of = { .i64 = 168 } }; + } + else if (kind == WASM_F32) { + 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 = 2.71828 } }; + } + else if (kind == WASM_EXTERNREF) { + return wasm_val_t{ .kind = WASM_EXTERNREF, + .of = { .foreign = 0xabcddead } }; + } + // 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 } }; + } +} +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 +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); + + 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); + } + + /* build results storage */ + 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); + 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.data(), param_count, args.data()); + if (!ret) { + const char *exception = wasm_runtime_get_exception(inst); + if (!exception) { + std::cout << "[EXECUTION] " << export_type.name + << "() failed. No exception info." << std::endl; + } + else { + std::cout << "[EXECUTION] " << export_type.name << "() failed. " + << exception << std::endl; + } + } + + wasm_runtime_clear_exception(inst); + } + + return true; +} + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { @@ -43,7 +186,7 @@ LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) return 0; } - std::cout << "PASS" << std::endl; + execute_export_functions(module, inst); wasm_runtime_deinstantiate(inst); wasm_runtime_unload(module);