diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index aa838a736e..9fd73520d5 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -141,6 +141,8 @@ enum TEMcodes : TERUnderlyingType { temARRAY_TOO_LARGE, temBAD_TRANSFER_FEE, temINVALID_INNER_BATCH, + + temBAD_WASM, }; //------------------------------------------------------------------------------ diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index 85b0d70259..35cf23d50e 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -223,6 +223,7 @@ transResults() MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."), MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."), MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."), + MAKE_ERROR(temBAD_WASM, "Malformed: Provided WASM code is invalid."), MAKE_ERROR(terRETRY, "Retry transaction."), MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."), diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 197fab8aaa..23b2c29c78 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1843,6 +1843,29 @@ struct Escrow_test : public beast::unit_test::suite ter(temMALFORMED)); env.close(); } + + { + // FinishFunction nonexistent host function + // pub fn finish() -> bool { + // unsafe { host_lib::bad() >= 5 } + // } + auto const badWasmHex = + "0061736d010000000105016000017f02100108686f73745f6c696203626164" + "00000302010005030100100611027f00418080c0000b7f00418080c0000b07" + "2e04066d656d6f727902000666696e69736800010a5f5f646174615f656e64" + "03000b5f5f686561705f6261736503010a09010700100041044a0b004d0970" + "726f64756365727302086c616e6775616765010452757374000c70726f6365" + "737365642d6279010572757374631d312e38352e3120283465623136313235" + "3020323032352d30332d31352900490f7461726765745f6665617475726573" + "042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f72" + "65666572656e63652d74797065732b0a6d756c746976616c7565"; + env(escrowCreate, + escrow::finish_function(badWasmHex), + escrow::cancel_time(env.now() + 100s), + fee(txnFees), + ter(temBAD_WASM)); + env.close(); + } } void diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index 19b7946f5b..52d3083561 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -1188,15 +1188,55 @@ struct Wasm_test : public beast::unit_test::suite testcase("bad wasm test"); using namespace test::jtx; - Env env{*this}; + Env env{*this}; HostFunctions hfs; - auto wasmHex = "00000000"; - auto wasmStr = boost::algorithm::unhex(std::string(wasmHex)); - std::vector wasm(wasmStr.begin(), wasmStr.end()); - std::string funcName("mock_escrow"); - auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal); - BEAST_EXPECT(!re && re.error()); + + { + auto wasmHex = "00000000"; + auto wasmStr = boost::algorithm::unhex(std::string(wasmHex)); + std::vector wasm(wasmStr.begin(), wasmStr.end()); + std::string funcName("mock_escrow"); + + auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal); + BEAST_EXPECT(!re); + } + + { + auto wasmHex = "00112233445566778899AA"; + auto wasmStr = boost::algorithm::unhex(std::string(wasmHex)); + std::vector wasm(wasmStr.begin(), wasmStr.end()); + std::string funcName("mock_escrow"); + + auto const re = + preflightEscrowWasm(wasm, funcName, {}, &hfs, env.journal); + BEAST_EXPECT(!isTesSuccess(re)); + } + + { + // FinishFunction wrong function name + // pub fn bad() -> bool { + // unsafe { host_lib::getLedgerSqn() >= 5 } + // } + auto const badWasmHex = + "0061736d010000000105016000017f02190108686f73745f6c69620c6765" + "744c656467657253716e00000302010005030100100611027f00418080c0" + "000b7f00418080c0000b072b04066d656d6f727902000362616400010a5f" + "5f646174615f656e6403000b5f5f686561705f6261736503010a09010700" + "100041044a0b004d0970726f64756365727302086c616e67756167650104" + "52757374000c70726f6365737365642d6279010572757374631d312e3835" + "2e31202834656231363132353020323032352d30332d31352900490f7461" + "726765745f6665617475726573042b0f6d757461626c652d676c6f62616c" + "732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a" + "6d756c746976616c7565"; + auto wasmStr = boost::algorithm::unhex(std::string(badWasmHex)); + std::vector wasm(wasmStr.begin(), wasmStr.end()); + std::string funcName("finish"); + + auto const re = + preflightEscrowWasm(wasm, funcName, {}, &hfs, env.journal); + BEAST_EXPECT(!isTesSuccess(re)); + } } void @@ -1319,14 +1359,11 @@ struct Wasm_test : public beast::unit_test::suite return occurrences; }; + auto const s = sink.messages().str(); BEAST_EXPECT( - countSubstr( - sink.messages().str(), "WAMR Error: failed to call func") == - 1); + countSubstr(s, "WAMR Error: failed to call func") == 1); BEAST_EXPECT( - countSubstr( - sink.messages().str(), - "WAMR Exception: wasm operand stack overflow") == 1); + countSubstr(s, "Exception: wasm operand stack overflow") > 0); } { diff --git a/src/xrpld/app/misc/WamrVM.cpp b/src/xrpld/app/misc/WamrVM.cpp index cefee723f5..9ad697b1c1 100644 --- a/src/xrpld/app/misc/WamrVM.cpp +++ b/src/xrpld/app/misc/WamrVM.cpp @@ -133,28 +133,22 @@ print_wasm_error(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog) auto j = jlog.error(); #endif - j << "WAMR Error: " << msg; + wasm_byte_vec_t error_message WASM_EMPTY_VEC; if (trap) - { - wasm_byte_vec_t error_message; - wasm_trap_message(trap, &error_message); - if (error_message.num_elems) - { - j << -#ifdef DEBUG_OUTPUT - "\nWAMR " -#else - "WAMR " -#endif - << error_message.data; - } - if (error_message.size) - wasm_byte_vec_delete(&error_message); - wasm_trap_delete(trap); + if (error_message.num_elems) + { + error_message.data[error_message.num_elems - 1] = 0; // just in case + j << "WAMR Error: " << msg << ", " << error_message.data; } + else + j << "WAMR Error: " << msg; + + if (error_message.size) + wasm_byte_vec_delete(&error_message); + wasm_trap_delete(trap); #ifdef DEBUG_OUTPUT j << std::endl; @@ -185,21 +179,21 @@ InstanceWrapper::init( if (!mi || trap) { print_wasm_error("can't create instance", trap, j); - throw std::runtime_error("WAMR can't create instance"); + throw std::runtime_error("can't create instance"); } wasm_instance_exports(mi.get(), expt); return mi; } InstanceWrapper::InstanceWrapper() - : exports{0, nullptr, 0, 0, nullptr} - , mod_inst(nullptr, &wasm_instance_delete) + : exports_{0, nullptr, 0, 0, nullptr} + , instance_(nullptr, &wasm_instance_delete) { } InstanceWrapper::InstanceWrapper(InstanceWrapper&& o) - : exports{0, nullptr, 0, 0, nullptr} - , mod_inst(nullptr, &wasm_instance_delete) + : exports_{0, nullptr, 0, 0, nullptr} + , instance_(nullptr, &wasm_instance_delete) { *this = std::move(o); } @@ -211,18 +205,18 @@ InstanceWrapper::InstanceWrapper( int64_t gas, wasm_extern_vec_t const& imports, beast::Journal j) - : exports WASM_EMPTY_VEC - , mod_inst(init(s, m, maxPages, &exports, imports, j)) - , exec_env(wasm_instance_exec_env(mod_inst.get())) + : exports_ WASM_EMPTY_VEC + , instance_(init(s, m, maxPages, &exports_, imports, j)) + , execEnv_(wasm_instance_exec_env(instance_.get())) , j_(j) { - wasm_runtime_set_instruction_count_limit(exec_env, gas); + wasm_runtime_set_instruction_count_limit(execEnv_, gas); } InstanceWrapper::~InstanceWrapper() { - if (exports.size) - wasm_extern_vec_delete(&exports); + if (exports_.size) + wasm_extern_vec_delete(&exports_); } InstanceWrapper& @@ -231,14 +225,14 @@ InstanceWrapper::operator=(InstanceWrapper&& o) if (this == &o) return *this; - if (exports.size) - wasm_extern_vec_delete(&exports); - exports = o.exports; - o.exports = {0, nullptr, 0, 0, nullptr}; + if (exports_.size) + wasm_extern_vec_delete(&exports_); + exports_ = o.exports_; + o.exports_ = {0, nullptr, 0, 0, nullptr}; - mod_inst = std::move(o.mod_inst); - exec_env = o.exec_env; - o.exec_env = nullptr; + instance_ = std::move(o.instance_); + execEnv_ = o.execEnv_; + o.execEnv_ = nullptr; j_ = o.j_; @@ -247,7 +241,7 @@ InstanceWrapper::operator=(InstanceWrapper&& o) InstanceWrapper::operator bool() const { - return static_cast(mod_inst); + return static_cast(instance_); } FuncInfo @@ -258,13 +252,13 @@ InstanceWrapper::getFunc( wasm_func_t* f = nullptr; wasm_functype_t* ft = nullptr; - if (!mod_inst) - throw std::runtime_error("WAMR no module instance"); + if (!instance_) + throw std::runtime_error("no instance"); if (!export_types.num_elems) - throw std::runtime_error("WAMR no export"); - if (export_types.num_elems != exports.num_elems) - throw std::runtime_error("WAMR invalid export"); + throw std::runtime_error("no export"); + if (export_types.num_elems != exports_.num_elems) + throw std::runtime_error("invalid export"); for (unsigned i = 0; i < export_types.num_elems; ++i) { @@ -276,9 +270,9 @@ InstanceWrapper::getFunc( { if (funcName == std::string_view(name->data, name->size - 1)) { - auto* exn(exports.data[i]); + auto* exn(exports_.data[i]); if (wasm_extern_kind(exn) != WASM_EXTERN_FUNC) - throw std::runtime_error("WAMR invalid export"); + throw std::runtime_error("invalid export"); ft = wasm_externtype_as_functype( const_cast(exn_type)); @@ -290,7 +284,7 @@ InstanceWrapper::getFunc( if (!f || !ft) throw std::runtime_error( - "WAMR can't find function <" + std::string(funcName) + ">"); + "can't find function <" + std::string(funcName) + ">"); return {f, ft}; } @@ -298,13 +292,13 @@ InstanceWrapper::getFunc( wmem InstanceWrapper::getMem() const { - if (!mod_inst) - throw std::runtime_error("WAMR no module instance"); + if (!instance_) + throw std::runtime_error("no instance"); wasm_memory_t* mem = nullptr; - for (unsigned i = 0; i < exports.num_elems; ++i) + for (unsigned i = 0; i < exports_.num_elems; ++i) { - auto* e(exports.data[i]); + auto* e(exports_.data[i]); if (wasm_extern_kind(e) == WASM_EXTERN_MEMORY) { mem = wasm_extern_as_memory(e); @@ -313,7 +307,7 @@ InstanceWrapper::getMem() const } if (!mem) - throw std::runtime_error("WAMR no memory exported"); + throw std::runtime_error("no memory exported"); return { reinterpret_cast(wasm_memory_data(mem)), @@ -323,7 +317,7 @@ InstanceWrapper::getMem() const std::int64_t InstanceWrapper::getGas() const { - return exec_env ? wasm_runtime_get_instruction_count_limit(exec_env) : 0; + return execEnv_ ? wasm_runtime_get_instruction_count_limit(execEnv_) : 0; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -339,22 +333,20 @@ ModuleWrapper::init(wasm_store_t* s, Bytes const& wasmBin, beast::Journal j) nullptr}; ModulePtr m = ModulePtr(wasm_module_new(s, &code), &wasm_module_delete); if (!m) - { - print_wasm_error("can't create module", nullptr, j); - throw std::runtime_error("WAMR can't create module"); - } + throw std::runtime_error("can't create module"); + return m; } ModuleWrapper::ModuleWrapper() - : module(nullptr, &wasm_module_delete) - , export_types{0, nullptr, 0, 0, nullptr} + : module_(nullptr, &wasm_module_delete) + , exportTypes_{0, nullptr, 0, 0, nullptr} { } ModuleWrapper::ModuleWrapper(ModuleWrapper&& o) - : module(nullptr, &wasm_module_delete) - , export_types{0, nullptr, 0, 0, nullptr} + : module_(nullptr, &wasm_module_delete) + , exportTypes_{0, nullptr, 0, 0, nullptr} { *this = std::move(o); } @@ -367,11 +359,11 @@ ModuleWrapper::ModuleWrapper( int64_t gas, std::vector const& imports, beast::Journal j) - : module(init(s, wasmBin, j)) - , export_types{0, nullptr, 0, 0, nullptr} + : module_(init(s, wasmBin, j)) + , exportTypes_{0, nullptr, 0, 0, nullptr} , j_(j) { - wasm_module_exports(module.get(), &export_types); + wasm_module_exports(module_.get(), &exportTypes_); if (instantiate) { auto wimports = buildImports(s, imports); @@ -381,8 +373,8 @@ ModuleWrapper::ModuleWrapper( ModuleWrapper::~ModuleWrapper() { - if (export_types.size) - wasm_exporttype_vec_delete(&export_types); + if (exportTypes_.size) + wasm_exporttype_vec_delete(&exportTypes_); } ModuleWrapper& @@ -391,12 +383,12 @@ ModuleWrapper::operator=(ModuleWrapper&& o) if (this == &o) return *this; - module = std::move(o.module); - mod_inst = std::move(o.mod_inst); - if (export_types.size) - wasm_exporttype_vec_delete(&export_types); - export_types = o.export_types; - o.export_types = {0, nullptr, 0, 0, nullptr}; + module_ = std::move(o.module_); + instanceWrap_ = std::move(o.instanceWrap_); + if (exportTypes_.size) + wasm_exporttype_vec_delete(&exportTypes_); + exportTypes_ = o.exportTypes_; + o.exportTypes_ = {0, nullptr, 0, 0, nullptr}; j_ = o.j_; return *this; @@ -404,7 +396,7 @@ ModuleWrapper::operator=(ModuleWrapper&& o) ModuleWrapper::operator bool() const { - return mod_inst; + return instanceWrap_; } void @@ -437,7 +429,7 @@ ModuleWrapper::makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp) v.data[i] = wasm_valtype_new_f64(); break; default: - throw std::runtime_error("WAMR Invalid import type"); + throw std::runtime_error("invalid import type"); } } } @@ -464,7 +456,7 @@ ModuleWrapper::makeImpReturn(wasm_valtype_vec_t& v, WasmImportFunc const& imp) v.data[0] = wasm_valtype_new_f64(); break; default: - throw std::runtime_error("WAMR Invalid return type"); + throw std::runtime_error("invalid return type"); } } else @@ -477,7 +469,7 @@ ModuleWrapper::buildImports( std::vector const& imports) { wasm_importtype_vec_t importTypes = WASM_EMPTY_VEC; - wasm_module_imports(module.get(), &importTypes); + wasm_module_imports(module_.get(), &importTypes); std:: unique_ptr itDeleter(&importTypes, &wasm_importtype_vec_delete); @@ -572,19 +564,19 @@ ModuleWrapper::buildImports( FuncInfo ModuleWrapper::getFunc(std::string_view funcName) const { - return mod_inst.getFunc(funcName, export_types); + return instanceWrap_.getFunc(funcName, exportTypes_); } wmem ModuleWrapper::getMem() const { - return mod_inst.getMem(); + return instanceWrap_.getMem(); } InstanceWrapper const& ModuleWrapper::getInstance(int) const { - return mod_inst; + return instanceWrap_; } int @@ -594,7 +586,7 @@ ModuleWrapper::addInstance( int64_t gas, wasm_extern_vec_t const& imports) { - mod_inst = {s, module.get(), maxPages, gas, imports, j_}; + instanceWrap_ = {s, module_.get(), maxPages, gas, imports, j_}; return 0; } @@ -611,7 +603,7 @@ ModuleWrapper::addInstance( std::int64_t ModuleWrapper::getGas() { - return mod_inst.getGas(); + return instanceWrap_.getGas(); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -625,8 +617,8 @@ ModuleWrapper::getGas() // } WamrEngine::WamrEngine() - : engine(wasm_engine_new(), &wasm_engine_delete) - , store(nullptr, &wasm_store_delete) + : engine_(wasm_engine_new(), &wasm_engine_delete) + , store_(nullptr, &wasm_store_delete) { wasm_runtime_set_default_running_mode(Mode_Interp); wasm_runtime_set_log_level(WASM_LOG_LEVEL_FATAL); @@ -640,19 +632,16 @@ WamrEngine::addModule( int64_t gas, std::vector const& imports) { - module.reset(); - store.reset(); // to free the memory before creating new store - store = {wasm_store_new(engine.get()), &wasm_store_delete}; - module = std::make_unique( - store.get(), wasmCode, instantiate, defMaxPages, gas, imports, j_); + moduleWrap_.reset(); + store_.reset(); // to free the memory before creating new store + store_ = {wasm_store_new(engine_.get()), &wasm_store_delete}; + moduleWrap_ = std::make_unique( + store_.get(), wasmCode, instantiate, defMaxPages_, gas, imports, j_); - if (!module) - { - print_wasm_error("can't create module wrapper", nullptr, j_); - throw std::runtime_error("WAMR can't create module wrapper"); - } + if (!moduleWrap_) + throw std::runtime_error("can't create module wrapper"); - return module ? 0 : -1; + return moduleWrap_ ? 0 : -1; } // int @@ -664,7 +653,7 @@ WamrEngine::addModule( FuncInfo WamrEngine::getFunc(std::string_view funcName) { - return module->getFunc(funcName); + return moduleWrap_->getFunc(funcName); } std::vector @@ -699,6 +688,8 @@ WamrEngine::convertParams(std::vector const& params) } break; default: + throw std::runtime_error( + "unknown parameter type: " + std::to_string(p.type)); break; } } @@ -706,6 +697,25 @@ WamrEngine::convertParams(std::vector const& params) return v; } +int +WamrEngine::compareParamTypes( + wasm_valtype_vec_t const* ftp, + std::vector const& p) +{ + if (ftp->num_elems != p.size()) + return std::min(ftp->num_elems, p.size()); + + for (unsigned i = 0; i < ftp->num_elems; ++i) + { + auto const t1 = wasm_valtype_kind(ftp->data[i]); + auto const t2 = p[i].kind; + if (t1 != t2) + return i; + } + + return -1; +} + void WamrEngine::add_param(std::vector& in, int32_t p) { @@ -761,13 +771,6 @@ WamrEngine::call(FuncInfo const& f, std::vector& in) // if (NR) { wasm_val_vec_new_uninitialized(&ret, NR); // // wasm_val_vec_new(&ret, NR, &rs[0]); // ret = WASM_ARRAY_VEC(rs); } - auto const* ftp = wasm_functype_params(f.second); - if (ftp->num_elems != in.size()) - { - print_wasm_error("invalid num of params to call func", nullptr, j_); - return ret; - } - wasm_val_vec_t const inv = in.empty() ? wasm_val_vec_t WASM_EMPTY_VEC : wasm_val_vec_t{ @@ -877,7 +880,7 @@ WamrEngine::run( } catch (...) { - print_wasm_error(std::string("unknown exception"), nullptr, j_); + print_wasm_error(std::string("exception: unknown"), nullptr, j_); } return Unexpected(tecFAILED_PROCESSING); } @@ -891,15 +894,8 @@ WamrEngine::runHlp( HostFunctions* hfs, int64_t gas) { - // #ifdef DEBUG_OUTPUT - // auto& j = std::cerr; - // #else - // auto j = j_.debug(); - // #endif - // currently only 1 module support, possible parallel UT run - static std::mutex m; - std::lock_guard lg(m); + std::lock_guard lg(m_); // Create and instantiate the module. if (!wasmCode.empty()) @@ -907,65 +903,130 @@ WamrEngine::runHlp( [[maybe_unused]] int const m = addModule(wasmCode, true, gas, imports); } - if (!module || !module->mod_inst) - { - print_wasm_error("no instance to run", nullptr, j_); - return Unexpected(tecFAILED_PROCESSING); - } + if (!moduleWrap_ || !moduleWrap_->instanceWrap_) + throw std::runtime_error("no instance"); if (hfs) hfs->setRT(&getRT()); // Call main - auto f = getFunc(!funcName.empty() ? funcName : "_start"); + auto const f = getFunc(!funcName.empty() ? funcName : "_start"); + auto const* ftp = wasm_functype_params(f.second); + + // not const because passed directly to wamr function (which accept non + // const) auto p = convertParams(params); - auto res = call<1>(f, p); + + if (int const comp = compareParamTypes(ftp, p); comp >= 0) + throw std::runtime_error( + "invalid parameter type #" + std::to_string(comp)); + + auto const res = call<1>(f, p); if (res.f) - { - return Unexpected(tecFAILED_PROCESSING); - } + throw std::runtime_error("<" + std::string(funcName) + "> failed"); else if (!res.r.num_elems) - { - print_wasm_error( - "<" + std::string(funcName) + "> return nothing", nullptr, j_); - return Unexpected(tecFAILED_PROCESSING); - } + throw std::runtime_error( + "<" + std::string(funcName) + "> return nothing"); assert(res.r.data[0].kind == WASM_I32); if (gas == -1) gas = std::numeric_limits::max(); - WasmResult const ret{res.r.data[0].of.i32, gas - module->getGas()}; + WasmResult const ret{ + res.r.data[0].of.i32, gas - moduleWrap_->getGas()}; + // #ifdef DEBUG_OUTPUT + // auto& j = std::cerr; + // #else + // auto j = j_.debug(); + // #endif // j << "WAMR Res: " << ret.result << " cost: " << ret.cost << std::endl; + return ret; } +NotTEC +WamrEngine::check( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params, + std::vector const& imports, + beast::Journal j) +{ + j_ = j; + wasm_runtime_set_log_level( + std::min(getLogLevel(j_.sink().threshold()), WASM_LOG_LEVEL_ERROR)); + try + { + return checkHlp(wasmCode, funcName, params, imports); + } + catch (std::exception const& e) + { + print_wasm_error(std::string("exception: ") + e.what(), nullptr, j_); + } + catch (...) + { + print_wasm_error(std::string("exception: unknown"), nullptr, j_); + } + + return temBAD_WASM; +} + +NotTEC +WamrEngine::checkHlp( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params, + std::vector const& imports) +{ + // currently only 1 module support, possible parallel UT run + std::lock_guard lg(m_); + + // Create and instantiate the module. + 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"); + + // 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 p = convertParams(params); + + if (int const comp = compareParamTypes(ftp, p); comp >= 0) + throw std::runtime_error( + "invalid parameter type #" + std::to_string(comp)); + + return tesSUCCESS; +} + std::int32_t WamrEngine::initMaxPages(std::int32_t def) { - defMaxPages = def; + defMaxPages_ = def; return def; } std::int64_t WamrEngine::getGas() { - return module ? module->getGas() : 0; + return moduleWrap_ ? moduleWrap_->getGas() : 0; } wmem WamrEngine::getMem() const { - return module ? module->getMem() : wmem(); + return moduleWrap_ ? moduleWrap_->getMem() : wmem(); } InstanceWrapper const& WamrEngine::getRT(int m, int i) { - if (!module) - throw std::runtime_error("WAMR no module"); - return module->getInstance(i); + if (!moduleWrap_) + throw std::runtime_error("no module"); + return moduleWrap_->getInstance(i); } int32_t @@ -976,7 +1037,7 @@ WamrEngine::allocate(int32_t sz) if (res.f || !res.r.num_elems || (res.r.data[0].kind != WASM_I32) || !res.r.data[0].of.i32) throw std::runtime_error( - "WAMR can't allocate memory, " + std::to_string(sz) + " bytes"); + "can't allocate memory, " + std::to_string(sz) + " bytes"); return res.r.data[0].of.i32; } @@ -988,7 +1049,7 @@ WamrEngine::newTrap(std::string_view txt) if (!txt.empty()) wasm_name_new(&msg, txt.size(), txt.data()); - return wasm_trap_new(store.get(), &msg); + return wasm_trap_new(store_.get(), &msg); } beast::Journal diff --git a/src/xrpld/app/misc/WamrVM.h b/src/xrpld/app/misc/WamrVM.h index 57c65504e1..3be35c673e 100644 --- a/src/xrpld/app/misc/WamrVM.h +++ b/src/xrpld/app/misc/WamrVM.h @@ -28,17 +28,20 @@ namespace ripple { struct WamrResult { wasm_val_vec_t r; - bool f; + bool f; // failure flag + WamrResult(unsigned N = 0) : r{0, nullptr, 0, 0, nullptr}, f(false) { if (N) wasm_val_vec_new_uninitialized(&r, N); } + ~WamrResult() { if (r.size) wasm_val_vec_delete(&r); } + WamrResult(WamrResult const&) = delete; WamrResult& operator=(WamrResult const&) = delete; @@ -47,6 +50,7 @@ struct WamrResult { *this = std::move(o); } + WamrResult& operator=(WamrResult&& o) { @@ -67,9 +71,9 @@ using FuncInfo = std::pair; struct InstanceWrapper { - wasm_extern_vec_t exports; - InstancePtr mod_inst; - wasm_exec_env_t exec_env = nullptr; + wasm_extern_vec_t exports_; + InstancePtr instance_; + wasm_exec_env_t execEnv_ = nullptr; beast::Journal j_ = beast::Journal(beast::Journal::getNullSink()); private: @@ -116,9 +120,9 @@ public: struct ModuleWrapper { - ModulePtr module; - InstanceWrapper mod_inst; - wasm_exporttype_vec_t export_types; + ModulePtr module_; + InstanceWrapper instanceWrap_; + wasm_exporttype_vec_t exportTypes_; beast::Journal j_ = beast::Journal(beast::Journal::getNullSink()); private: @@ -171,12 +175,14 @@ private: class WamrEngine { - std::unique_ptr engine; - std::unique_ptr store; - std::unique_ptr module; - std::int32_t defMaxPages = -1; + std::unique_ptr engine_; + std::unique_ptr store_; + std::unique_ptr moduleWrap_; + std::int32_t defMaxPages_ = -1; beast::Journal j_ = beast::Journal(beast::Journal::getNullSink()); + std::mutex m_; // 1 instance mutex + public: WamrEngine(); ~WamrEngine() = default; @@ -190,6 +196,14 @@ public: int64_t gas, beast::Journal j); + NotTEC + check( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params, + std::vector const& imports, + beast::Journal j); + std::int32_t initMaxPages(std::int32_t def); @@ -222,6 +236,13 @@ private: HostFunctions* hfs, int64_t gas); + NotTEC + checkHlp( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params, + std::vector const& imports); + int addModule( Bytes const& wasmCode, @@ -247,9 +268,14 @@ private: std::vector convertParams(std::vector const& params); - void + static int + compareParamTypes( + wasm_valtype_vec_t const* ftp, + std::vector const& p); + + static void add_param(std::vector& in, int32_t p); - void + static void add_param(std::vector& in, int64_t p); template diff --git a/src/xrpld/app/misc/WasmVM.cpp b/src/xrpld/app/misc/WasmVM.cpp index dbfcfe549c..b21007b9d8 100644 --- a/src/xrpld/app/misc/WasmVM.cpp +++ b/src/xrpld/app/misc/WasmVM.cpp @@ -29,19 +29,9 @@ namespace ripple { -Expected -runEscrowWasm( - Bytes const& wasmCode, - std::string_view funcName, - std::vector const& params, - HostFunctions* hfs, - int64_t gasLimit, - beast::Journal j) +static std::vector +createImports(HostFunctions* hfs) { - // create VM and set cost limit - auto& vm = WasmEngine::instance(); - vm.initMaxPages(MAX_PAGES); - std::vector imports; if (hfs) @@ -107,11 +97,27 @@ runEscrowWasm( WASM_IMPORT_FUNC2(imports, traceNum, "trace_num", hfs); } - auto ret = vm.run( + return imports; +} + +Expected +runEscrowWasm( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params, + HostFunctions* hfs, + int64_t gasLimit, + beast::Journal j) +{ + // create VM and set cost limit + auto& vm = WasmEngine::instance(); + vm.initMaxPages(MAX_PAGES); + + auto const ret = vm.run( wasmCode, funcName, params, - imports, + createImports(hfs), hfs, gasLimit, hfs ? hfs->getJournal() : j); @@ -130,6 +136,28 @@ runEscrowWasm( return EscrowResult{ret->result > 0, ret->cost}; } +NotTEC +preflightEscrowWasm( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params, + HostFunctions* hfs, + beast::Journal j) +{ + // create VM and set cost limit + auto& vm = WasmEngine::instance(); + vm.initMaxPages(MAX_PAGES); + + auto const ret = vm.check( + wasmCode, + funcName, + params, + createImports(hfs), + hfs ? hfs->getJournal() : j); + + return ret; +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// WasmEngine::WasmEngine() : impl(std::make_unique()) @@ -156,6 +184,17 @@ WasmEngine::run( return impl->run(wasmCode, funcName, params, imports, hfs, gasLimit, j); } +NotTEC +WasmEngine::check( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params, + std::vector const& imports, + beast::Journal j) +{ + return impl->check(wasmCode, funcName, params, imports, j); +} + std::int32_t WasmEngine::initMaxPages(std::int32_t def) { diff --git a/src/xrpld/app/misc/WasmVM.h b/src/xrpld/app/misc/WasmVM.h index b12d7ecc7e..7c68a18696 100644 --- a/src/xrpld/app/misc/WasmVM.h +++ b/src/xrpld/app/misc/WasmVM.h @@ -65,6 +65,14 @@ public: int64_t gasLimit = -1, beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); + NotTEC + check( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params = {}, + std::vector const& imports = {}, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); + std::int32_t initMaxPages(std::int32_t def); @@ -87,4 +95,12 @@ runEscrowWasm( int64_t gasLimit = -1, beast::Journal j = beast::Journal(beast::Journal::getNullSink())); +NotTEC +preflightEscrowWasm( + Bytes const& wasmCode, + std::string_view funcName, + std::vector const& params = {}, + HostFunctions* hfs = nullptr, + beast::Journal j = beast::Journal(beast::Journal::getNullSink())); + } // namespace ripple diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 6f52d1a4f2..7639b4efa0 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -231,7 +231,14 @@ EscrowCreate::preflight(PreflightContext const& ctx) << "EscrowCreate.FinishFunction bad size " << code.size(); return temMALFORMED; } - // TODO: add check to ensure this is valid WASM code + + HostFunctions mock; + auto const re = preflightEscrowWasm(code, "finish", {}, &mock); + if (!isTesSuccess(re)) + { + JLOG(ctx.j.debug()) << "EscrowCreate.FinishFunction bad WASM"; + return re; + } } return preflight2(ctx);