From 82c7c7d82fd990e05ad803e10beabdbc8b47b028 Mon Sep 17 00:00:00 2001 From: Peng Wang Date: Mon, 5 Jan 2026 11:57:23 -0500 Subject: [PATCH] fix start function loop --- src/test/app/Wasm_test.cpp | 33 +++++++++++++++++++ src/test/app/wasm_fixtures/fixtures.cpp | 4 +++ src/test/app/wasm_fixtures/fixtures.h | 1 + src/test/app/wasm_fixtures/wat/start_loop.wat | 22 +++++++++++++ src/xrpld/app/wasm/WasmiVM.h | 4 +++ src/xrpld/app/wasm/detail/WasmiVM.cpp | 33 +++++++++++++++---- 6 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/test/app/wasm_fixtures/wat/start_loop.wat diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index 367afbafd0a..dba390c93e4 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -850,6 +850,38 @@ struct Wasm_test : public beast::unit_test::suite runFinishFunction(localVariableBombHex).has_value() == false); } + void + testStartFunctionLoop() + { + testcase("infinite loop in start function"); + + using namespace test::jtx; + Env env(*this); + + auto wasmStr = boost::algorithm::unhex(startLoopHex); + Bytes wasm(wasmStr.begin(), wasmStr.end()); + TestLedgerDataProvider ledgerDataProvider(env); + ImportVec imports; + + auto& engine = WasmEngine::instance(); + auto checkRes = engine.check( + wasm, "finish", {}, imports, &ledgerDataProvider, env.journal); + BEAST_EXPECTS( + checkRes == tesSUCCESS, std::to_string(TERtoInt(checkRes))); + + auto re = engine.run( + wasm, + ESCROW_FUNCTION_NAME, + {}, + imports, + &ledgerDataProvider, + 1'000'000, + env.journal); + BEAST_EXPECTS( + re.error() == tecFAILED_PROCESSING, + std::to_string(TERtoInt(re.error()))); + } + void run() override { @@ -878,6 +910,7 @@ struct Wasm_test : public beast::unit_test::suite testWasmWasi(); testWasmSectionCorruption(); + testStartFunctionLoop(); // perfTest(); } }; diff --git a/src/test/app/wasm_fixtures/fixtures.cpp b/src/test/app/wasm_fixtures/fixtures.cpp index aa30cf08e78..740638ba610 100644 --- a/src/test/app/wasm_fixtures/fixtures.cpp +++ b/src/test/app/wasm_fixtures/fixtures.cpp @@ -1340,3 +1340,7 @@ extern std::string const infiniteLoopWasmHex = "303861373930636664623432626432343732302900490f7461726765745f66656174757265" "73042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f726566657265" "6e63652d74797065732b0a6d756c746976616c7565"; + +extern std::string const startLoopHex = + "0061736d010000000108026000006000017f03030200010712020573746172740000066669" + "6e69736800010801000a0e02070003400c000b0b040041010b"; diff --git a/src/test/app/wasm_fixtures/fixtures.h b/src/test/app/wasm_fixtures/fixtures.h index ef1ecd9de5c..7da8fcb4dc6 100644 --- a/src/test/app/wasm_fixtures/fixtures.h +++ b/src/test/app/wasm_fixtures/fixtures.h @@ -77,3 +77,4 @@ extern std::string const invalidSectionIdHex; extern std::string const localVariableBombHex; extern std::string const infiniteLoopWasmHex; +extern std::string const startLoopHex; diff --git a/src/test/app/wasm_fixtures/wat/start_loop.wat b/src/test/app/wasm_fixtures/wat/start_loop.wat new file mode 100644 index 00000000000..c00d173ea45 --- /dev/null +++ b/src/test/app/wasm_fixtures/wat/start_loop.wat @@ -0,0 +1,22 @@ +(module + ;; Function 1: The Infinite Loop + (func $run_forever + (loop $infinite + br $infinite + ) + ) + + ;; Function 2: Finish + (func $finish (result i32) + i32.const 1 + ) + + ;; 1. EXPORT the functions (optional, if you want to call them later) + (export "start" (func $run_forever)) + (export "finish" (func $finish)) + + ;; 2. The special start section + ;; This tells the VM: "Run function $run_forever immediately + ;; when this module is instantiated." + (start $run_forever) +) diff --git a/src/xrpld/app/wasm/WasmiVM.h b/src/xrpld/app/wasm/WasmiVM.h index 06e18ef29e7..c39b577c46f 100644 --- a/src/xrpld/app/wasm/WasmiVM.h +++ b/src/xrpld/app/wasm/WasmiVM.h @@ -179,6 +179,10 @@ struct ModuleWrapper FuncInfo getFunc(std::string_view funcName) const; + + wasm_functype_t* + getFuncType(std::string_view funcName) const; + wmem getMem() const; diff --git a/src/xrpld/app/wasm/detail/WasmiVM.cpp b/src/xrpld/app/wasm/detail/WasmiVM.cpp index 40ec5f81b6f..016adeac5a6 100644 --- a/src/xrpld/app/wasm/detail/WasmiVM.cpp +++ b/src/xrpld/app/wasm/detail/WasmiVM.cpp @@ -249,9 +249,9 @@ ModuleWrapper::ModuleWrapper( : module_(init(s, wasmBin, j)), j_(j) { wasm_module_exports(module_.get(), &exportTypes_.vec_); + auto wimports = buildImports(s, imports); if (instantiate) { - auto wimports = buildImports(s, imports); addInstance(s, wimports); } } @@ -427,6 +427,26 @@ ModuleWrapper::getFunc(std::string_view funcName) const return instanceWrap_.getFunc(funcName, exportTypes_); } +wasm_functype_t* +ModuleWrapper::getFuncType(std::string_view funcName) const +{ + for (size_t i = 0; i < exportTypes_.vec_.size; i++) + { + auto const* exp_type(exportTypes_.vec_.data[i]); + wasm_name_t const* name = wasm_exporttype_name(exp_type); + wasm_externtype_t const* exn_type = wasm_exporttype_type(exp_type); + if (wasm_externtype_kind(exn_type) == WASM_EXTERN_FUNC && + funcName == std::string_view(name->data, name->size)) + { + return wasm_externtype_as_functype( + const_cast(exn_type)); + } + } + + throw std::runtime_error( + "can't find function <" + std::string(funcName) + ">"); +} + wmem ModuleWrapper::getMem() const { @@ -889,13 +909,14 @@ WasmiEngine::checkHlp( if (wasmCode.empty()) throw std::runtime_error("empty nodule"); - int const m = addModule(wasmCode, true, -1, imports); - if ((m < 0) || !moduleWrap_ || !moduleWrap_->instanceWrap_) - throw std::runtime_error("no instance"); // LCOV_EXCL_LINE + int const m = addModule(wasmCode, false, -1, imports); + if ((m < 0) || !moduleWrap_) + throw std::runtime_error("no module"); // LCOV_EXCL_LINE // Looking for a func and compare parameter types - auto const f = getFunc(!funcName.empty() ? funcName : "_start"); - auto const* ftp = wasm_functype_params(f.second); + auto const f = + moduleWrap_->getFuncType(!funcName.empty() ? funcName : "_start"); + auto const* ftp = wasm_functype_params(f); auto const p = convertParams(params); if (int const comp = compareParamTypes(ftp, p); comp >= 0)