preflight checks for wasm (#5517)

This commit is contained in:
Olek
2025-06-30 09:34:38 -04:00
committed by GitHub
parent 1cd16fab87
commit 463acf51b5
9 changed files with 385 additions and 173 deletions

View File

@@ -141,6 +141,8 @@ enum TEMcodes : TERUnderlyingType {
temARRAY_TOO_LARGE, temARRAY_TOO_LARGE,
temBAD_TRANSFER_FEE, temBAD_TRANSFER_FEE,
temINVALID_INNER_BATCH, temINVALID_INNER_BATCH,
temBAD_WASM,
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -223,6 +223,7 @@ transResults()
MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."), MAKE_ERROR(temARRAY_TOO_LARGE, "Malformed: Array is too large."),
MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."), MAKE_ERROR(temBAD_TRANSFER_FEE, "Malformed: Transfer fee is outside valid range."),
MAKE_ERROR(temINVALID_INNER_BATCH, "Malformed: Invalid inner batch transaction."), 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(terRETRY, "Retry transaction."),
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."), MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),

View File

@@ -1843,6 +1843,29 @@ struct Escrow_test : public beast::unit_test::suite
ter(temMALFORMED)); ter(temMALFORMED));
env.close(); 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 void

View File

@@ -1188,15 +1188,55 @@ struct Wasm_test : public beast::unit_test::suite
testcase("bad wasm test"); testcase("bad wasm test");
using namespace test::jtx; using namespace test::jtx;
Env env{*this};
Env env{*this};
HostFunctions hfs; HostFunctions hfs;
{
auto wasmHex = "00000000"; auto wasmHex = "00000000";
auto wasmStr = boost::algorithm::unhex(std::string(wasmHex)); auto wasmStr = boost::algorithm::unhex(std::string(wasmHex));
std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end()); std::vector<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
std::string funcName("mock_escrow"); std::string funcName("mock_escrow");
auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal); auto re = runEscrowWasm(wasm, funcName, {}, &hfs, 15, env.journal);
BEAST_EXPECT(!re && re.error()); BEAST_EXPECT(!re);
}
{
auto wasmHex = "00112233445566778899AA";
auto wasmStr = boost::algorithm::unhex(std::string(wasmHex));
std::vector<uint8_t> 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<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
std::string funcName("finish");
auto const re =
preflightEscrowWasm(wasm, funcName, {}, &hfs, env.journal);
BEAST_EXPECT(!isTesSuccess(re));
}
} }
void void
@@ -1319,14 +1359,11 @@ struct Wasm_test : public beast::unit_test::suite
return occurrences; return occurrences;
}; };
auto const s = sink.messages().str();
BEAST_EXPECT( BEAST_EXPECT(
countSubstr( countSubstr(s, "WAMR Error: failed to call func") == 1);
sink.messages().str(), "WAMR Error: failed to call func") ==
1);
BEAST_EXPECT( BEAST_EXPECT(
countSubstr( countSubstr(s, "Exception: wasm operand stack overflow") > 0);
sink.messages().str(),
"WAMR Exception: wasm operand stack overflow") == 1);
} }
{ {

View File

@@ -133,28 +133,22 @@ print_wasm_error(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog)
auto j = jlog.error(); auto j = jlog.error();
#endif #endif
j << "WAMR Error: " << msg; wasm_byte_vec_t error_message WASM_EMPTY_VEC;
if (trap) if (trap)
{
wasm_byte_vec_t error_message;
wasm_trap_message(trap, &error_message); wasm_trap_message(trap, &error_message);
if (error_message.num_elems) if (error_message.num_elems)
{ {
j << error_message.data[error_message.num_elems - 1] = 0; // just in case
#ifdef DEBUG_OUTPUT j << "WAMR Error: " << msg << ", " << error_message.data;
"\nWAMR "
#else
"WAMR "
#endif
<< error_message.data;
} }
else
j << "WAMR Error: " << msg;
if (error_message.size) if (error_message.size)
wasm_byte_vec_delete(&error_message); wasm_byte_vec_delete(&error_message);
wasm_trap_delete(trap); wasm_trap_delete(trap);
}
#ifdef DEBUG_OUTPUT #ifdef DEBUG_OUTPUT
j << std::endl; j << std::endl;
@@ -185,21 +179,21 @@ InstanceWrapper::init(
if (!mi || trap) if (!mi || trap)
{ {
print_wasm_error("can't create instance", trap, j); 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); wasm_instance_exports(mi.get(), expt);
return mi; return mi;
} }
InstanceWrapper::InstanceWrapper() InstanceWrapper::InstanceWrapper()
: exports{0, nullptr, 0, 0, nullptr} : exports_{0, nullptr, 0, 0, nullptr}
, mod_inst(nullptr, &wasm_instance_delete) , instance_(nullptr, &wasm_instance_delete)
{ {
} }
InstanceWrapper::InstanceWrapper(InstanceWrapper&& o) InstanceWrapper::InstanceWrapper(InstanceWrapper&& o)
: exports{0, nullptr, 0, 0, nullptr} : exports_{0, nullptr, 0, 0, nullptr}
, mod_inst(nullptr, &wasm_instance_delete) , instance_(nullptr, &wasm_instance_delete)
{ {
*this = std::move(o); *this = std::move(o);
} }
@@ -211,18 +205,18 @@ InstanceWrapper::InstanceWrapper(
int64_t gas, int64_t gas,
wasm_extern_vec_t const& imports, wasm_extern_vec_t const& imports,
beast::Journal j) beast::Journal j)
: exports WASM_EMPTY_VEC : exports_ WASM_EMPTY_VEC
, mod_inst(init(s, m, maxPages, &exports, imports, j)) , instance_(init(s, m, maxPages, &exports_, imports, j))
, exec_env(wasm_instance_exec_env(mod_inst.get())) , execEnv_(wasm_instance_exec_env(instance_.get()))
, j_(j) , j_(j)
{ {
wasm_runtime_set_instruction_count_limit(exec_env, gas); wasm_runtime_set_instruction_count_limit(execEnv_, gas);
} }
InstanceWrapper::~InstanceWrapper() InstanceWrapper::~InstanceWrapper()
{ {
if (exports.size) if (exports_.size)
wasm_extern_vec_delete(&exports); wasm_extern_vec_delete(&exports_);
} }
InstanceWrapper& InstanceWrapper&
@@ -231,14 +225,14 @@ InstanceWrapper::operator=(InstanceWrapper&& o)
if (this == &o) if (this == &o)
return *this; return *this;
if (exports.size) if (exports_.size)
wasm_extern_vec_delete(&exports); wasm_extern_vec_delete(&exports_);
exports = o.exports; exports_ = o.exports_;
o.exports = {0, nullptr, 0, 0, nullptr}; o.exports_ = {0, nullptr, 0, 0, nullptr};
mod_inst = std::move(o.mod_inst); instance_ = std::move(o.instance_);
exec_env = o.exec_env; execEnv_ = o.execEnv_;
o.exec_env = nullptr; o.execEnv_ = nullptr;
j_ = o.j_; j_ = o.j_;
@@ -247,7 +241,7 @@ InstanceWrapper::operator=(InstanceWrapper&& o)
InstanceWrapper::operator bool() const InstanceWrapper::operator bool() const
{ {
return static_cast<bool>(mod_inst); return static_cast<bool>(instance_);
} }
FuncInfo FuncInfo
@@ -258,13 +252,13 @@ InstanceWrapper::getFunc(
wasm_func_t* f = nullptr; wasm_func_t* f = nullptr;
wasm_functype_t* ft = nullptr; wasm_functype_t* ft = nullptr;
if (!mod_inst) if (!instance_)
throw std::runtime_error("WAMR no module instance"); throw std::runtime_error("no instance");
if (!export_types.num_elems) if (!export_types.num_elems)
throw std::runtime_error("WAMR no export"); throw std::runtime_error("no export");
if (export_types.num_elems != exports.num_elems) if (export_types.num_elems != exports_.num_elems)
throw std::runtime_error("WAMR invalid export"); throw std::runtime_error("invalid export");
for (unsigned i = 0; i < export_types.num_elems; ++i) 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)) 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) 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( ft = wasm_externtype_as_functype(
const_cast<wasm_externtype_t*>(exn_type)); const_cast<wasm_externtype_t*>(exn_type));
@@ -290,7 +284,7 @@ InstanceWrapper::getFunc(
if (!f || !ft) if (!f || !ft)
throw std::runtime_error( throw std::runtime_error(
"WAMR can't find function <" + std::string(funcName) + ">"); "can't find function <" + std::string(funcName) + ">");
return {f, ft}; return {f, ft};
} }
@@ -298,13 +292,13 @@ InstanceWrapper::getFunc(
wmem wmem
InstanceWrapper::getMem() const InstanceWrapper::getMem() const
{ {
if (!mod_inst) if (!instance_)
throw std::runtime_error("WAMR no module instance"); throw std::runtime_error("no instance");
wasm_memory_t* mem = nullptr; 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) if (wasm_extern_kind(e) == WASM_EXTERN_MEMORY)
{ {
mem = wasm_extern_as_memory(e); mem = wasm_extern_as_memory(e);
@@ -313,7 +307,7 @@ InstanceWrapper::getMem() const
} }
if (!mem) if (!mem)
throw std::runtime_error("WAMR no memory exported"); throw std::runtime_error("no memory exported");
return { return {
reinterpret_cast<std::uint8_t*>(wasm_memory_data(mem)), reinterpret_cast<std::uint8_t*>(wasm_memory_data(mem)),
@@ -323,7 +317,7 @@ InstanceWrapper::getMem() const
std::int64_t std::int64_t
InstanceWrapper::getGas() const 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}; nullptr};
ModulePtr m = ModulePtr(wasm_module_new(s, &code), &wasm_module_delete); ModulePtr m = ModulePtr(wasm_module_new(s, &code), &wasm_module_delete);
if (!m) if (!m)
{ throw std::runtime_error("can't create module");
print_wasm_error("can't create module", nullptr, j);
throw std::runtime_error("WAMR can't create module");
}
return m; return m;
} }
ModuleWrapper::ModuleWrapper() ModuleWrapper::ModuleWrapper()
: module(nullptr, &wasm_module_delete) : module_(nullptr, &wasm_module_delete)
, export_types{0, nullptr, 0, 0, nullptr} , exportTypes_{0, nullptr, 0, 0, nullptr}
{ {
} }
ModuleWrapper::ModuleWrapper(ModuleWrapper&& o) ModuleWrapper::ModuleWrapper(ModuleWrapper&& o)
: module(nullptr, &wasm_module_delete) : module_(nullptr, &wasm_module_delete)
, export_types{0, nullptr, 0, 0, nullptr} , exportTypes_{0, nullptr, 0, 0, nullptr}
{ {
*this = std::move(o); *this = std::move(o);
} }
@@ -367,11 +359,11 @@ ModuleWrapper::ModuleWrapper(
int64_t gas, int64_t gas,
std::vector<WasmImportFunc> const& imports, std::vector<WasmImportFunc> const& imports,
beast::Journal j) beast::Journal j)
: module(init(s, wasmBin, j)) : module_(init(s, wasmBin, j))
, export_types{0, nullptr, 0, 0, nullptr} , exportTypes_{0, nullptr, 0, 0, nullptr}
, j_(j) , j_(j)
{ {
wasm_module_exports(module.get(), &export_types); wasm_module_exports(module_.get(), &exportTypes_);
if (instantiate) if (instantiate)
{ {
auto wimports = buildImports(s, imports); auto wimports = buildImports(s, imports);
@@ -381,8 +373,8 @@ ModuleWrapper::ModuleWrapper(
ModuleWrapper::~ModuleWrapper() ModuleWrapper::~ModuleWrapper()
{ {
if (export_types.size) if (exportTypes_.size)
wasm_exporttype_vec_delete(&export_types); wasm_exporttype_vec_delete(&exportTypes_);
} }
ModuleWrapper& ModuleWrapper&
@@ -391,12 +383,12 @@ ModuleWrapper::operator=(ModuleWrapper&& o)
if (this == &o) if (this == &o)
return *this; return *this;
module = std::move(o.module); module_ = std::move(o.module_);
mod_inst = std::move(o.mod_inst); instanceWrap_ = std::move(o.instanceWrap_);
if (export_types.size) if (exportTypes_.size)
wasm_exporttype_vec_delete(&export_types); wasm_exporttype_vec_delete(&exportTypes_);
export_types = o.export_types; exportTypes_ = o.exportTypes_;
o.export_types = {0, nullptr, 0, 0, nullptr}; o.exportTypes_ = {0, nullptr, 0, 0, nullptr};
j_ = o.j_; j_ = o.j_;
return *this; return *this;
@@ -404,7 +396,7 @@ ModuleWrapper::operator=(ModuleWrapper&& o)
ModuleWrapper::operator bool() const ModuleWrapper::operator bool() const
{ {
return mod_inst; return instanceWrap_;
} }
void void
@@ -437,7 +429,7 @@ ModuleWrapper::makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp)
v.data[i] = wasm_valtype_new_f64(); v.data[i] = wasm_valtype_new_f64();
break; break;
default: 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(); v.data[0] = wasm_valtype_new_f64();
break; break;
default: default:
throw std::runtime_error("WAMR Invalid return type"); throw std::runtime_error("invalid return type");
} }
} }
else else
@@ -477,7 +469,7 @@ ModuleWrapper::buildImports(
std::vector<WasmImportFunc> const& imports) std::vector<WasmImportFunc> const& imports)
{ {
wasm_importtype_vec_t importTypes = WASM_EMPTY_VEC; wasm_importtype_vec_t importTypes = WASM_EMPTY_VEC;
wasm_module_imports(module.get(), &importTypes); wasm_module_imports(module_.get(), &importTypes);
std:: std::
unique_ptr<wasm_importtype_vec_t, decltype(&wasm_importtype_vec_delete)> unique_ptr<wasm_importtype_vec_t, decltype(&wasm_importtype_vec_delete)>
itDeleter(&importTypes, &wasm_importtype_vec_delete); itDeleter(&importTypes, &wasm_importtype_vec_delete);
@@ -572,19 +564,19 @@ ModuleWrapper::buildImports(
FuncInfo FuncInfo
ModuleWrapper::getFunc(std::string_view funcName) const ModuleWrapper::getFunc(std::string_view funcName) const
{ {
return mod_inst.getFunc(funcName, export_types); return instanceWrap_.getFunc(funcName, exportTypes_);
} }
wmem wmem
ModuleWrapper::getMem() const ModuleWrapper::getMem() const
{ {
return mod_inst.getMem(); return instanceWrap_.getMem();
} }
InstanceWrapper const& InstanceWrapper const&
ModuleWrapper::getInstance(int) const ModuleWrapper::getInstance(int) const
{ {
return mod_inst; return instanceWrap_;
} }
int int
@@ -594,7 +586,7 @@ ModuleWrapper::addInstance(
int64_t gas, int64_t gas,
wasm_extern_vec_t const& imports) 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; return 0;
} }
@@ -611,7 +603,7 @@ ModuleWrapper::addInstance(
std::int64_t std::int64_t
ModuleWrapper::getGas() ModuleWrapper::getGas()
{ {
return mod_inst.getGas(); return instanceWrap_.getGas();
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -625,8 +617,8 @@ ModuleWrapper::getGas()
// } // }
WamrEngine::WamrEngine() WamrEngine::WamrEngine()
: engine(wasm_engine_new(), &wasm_engine_delete) : engine_(wasm_engine_new(), &wasm_engine_delete)
, store(nullptr, &wasm_store_delete) , store_(nullptr, &wasm_store_delete)
{ {
wasm_runtime_set_default_running_mode(Mode_Interp); wasm_runtime_set_default_running_mode(Mode_Interp);
wasm_runtime_set_log_level(WASM_LOG_LEVEL_FATAL); wasm_runtime_set_log_level(WASM_LOG_LEVEL_FATAL);
@@ -640,19 +632,16 @@ WamrEngine::addModule(
int64_t gas, int64_t gas,
std::vector<WasmImportFunc> const& imports) std::vector<WasmImportFunc> const& imports)
{ {
module.reset(); moduleWrap_.reset();
store.reset(); // to free the memory before creating new store store_.reset(); // to free the memory before creating new store
store = {wasm_store_new(engine.get()), &wasm_store_delete}; store_ = {wasm_store_new(engine_.get()), &wasm_store_delete};
module = std::make_unique<ModuleWrapper>( moduleWrap_ = std::make_unique<ModuleWrapper>(
store.get(), wasmCode, instantiate, defMaxPages, gas, imports, j_); store_.get(), wasmCode, instantiate, defMaxPages_, gas, imports, j_);
if (!module) if (!moduleWrap_)
{ throw std::runtime_error("can't create module wrapper");
print_wasm_error("can't create module wrapper", nullptr, j_);
throw std::runtime_error("WAMR can't create module wrapper");
}
return module ? 0 : -1; return moduleWrap_ ? 0 : -1;
} }
// int // int
@@ -664,7 +653,7 @@ WamrEngine::addModule(
FuncInfo FuncInfo
WamrEngine::getFunc(std::string_view funcName) WamrEngine::getFunc(std::string_view funcName)
{ {
return module->getFunc(funcName); return moduleWrap_->getFunc(funcName);
} }
std::vector<wasm_val_t> std::vector<wasm_val_t>
@@ -699,6 +688,8 @@ WamrEngine::convertParams(std::vector<WasmParam> const& params)
} }
break; break;
default: default:
throw std::runtime_error(
"unknown parameter type: " + std::to_string(p.type));
break; break;
} }
} }
@@ -706,6 +697,25 @@ WamrEngine::convertParams(std::vector<WasmParam> const& params)
return v; return v;
} }
int
WamrEngine::compareParamTypes(
wasm_valtype_vec_t const* ftp,
std::vector<wasm_val_t> 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 void
WamrEngine::add_param(std::vector<wasm_val_t>& in, int32_t p) WamrEngine::add_param(std::vector<wasm_val_t>& in, int32_t p)
{ {
@@ -761,13 +771,6 @@ WamrEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in)
// if (NR) { wasm_val_vec_new_uninitialized(&ret, NR); // // if (NR) { wasm_val_vec_new_uninitialized(&ret, NR); //
// wasm_val_vec_new(&ret, NR, &rs[0]); // ret = WASM_ARRAY_VEC(rs); } // 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 const inv = in.empty()
? wasm_val_vec_t WASM_EMPTY_VEC ? wasm_val_vec_t WASM_EMPTY_VEC
: wasm_val_vec_t{ : wasm_val_vec_t{
@@ -877,7 +880,7 @@ WamrEngine::run(
} }
catch (...) catch (...)
{ {
print_wasm_error(std::string("unknown exception"), nullptr, j_); print_wasm_error(std::string("exception: unknown"), nullptr, j_);
} }
return Unexpected<TER>(tecFAILED_PROCESSING); return Unexpected<TER>(tecFAILED_PROCESSING);
} }
@@ -891,15 +894,8 @@ WamrEngine::runHlp(
HostFunctions* hfs, HostFunctions* hfs,
int64_t gas) 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 // currently only 1 module support, possible parallel UT run
static std::mutex m; std::lock_guard<decltype(m_)> lg(m_);
std::lock_guard<decltype(m)> lg(m);
// Create and instantiate the module. // Create and instantiate the module.
if (!wasmCode.empty()) if (!wasmCode.empty())
@@ -907,65 +903,130 @@ WamrEngine::runHlp(
[[maybe_unused]] int const m = addModule(wasmCode, true, gas, imports); [[maybe_unused]] int const m = addModule(wasmCode, true, gas, imports);
} }
if (!module || !module->mod_inst) if (!moduleWrap_ || !moduleWrap_->instanceWrap_)
{ throw std::runtime_error("no instance");
print_wasm_error("no instance to run", nullptr, j_);
return Unexpected<TER>(tecFAILED_PROCESSING);
}
if (hfs) if (hfs)
hfs->setRT(&getRT()); hfs->setRT(&getRT());
// Call main // 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 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) if (res.f)
{ throw std::runtime_error("<" + std::string(funcName) + "> failed");
return Unexpected<TER>(tecFAILED_PROCESSING);
}
else if (!res.r.num_elems) else if (!res.r.num_elems)
{ throw std::runtime_error(
print_wasm_error( "<" + std::string(funcName) + "> return nothing");
"<" + std::string(funcName) + "> return nothing", nullptr, j_);
return Unexpected<TER>(tecFAILED_PROCESSING);
}
assert(res.r.data[0].kind == WASM_I32); assert(res.r.data[0].kind == WASM_I32);
if (gas == -1) if (gas == -1)
gas = std::numeric_limits<decltype(gas)>::max(); gas = std::numeric_limits<decltype(gas)>::max();
WasmResult<int32_t> const ret{res.r.data[0].of.i32, gas - module->getGas()}; WasmResult<int32_t> 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; // j << "WAMR Res: " << ret.result << " cost: " << ret.cost << std::endl;
return ret; return ret;
} }
NotTEC
WamrEngine::check(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> 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<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports)
{
// currently only 1 module support, possible parallel UT run
std::lock_guard<decltype(m_)> 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 std::int32_t
WamrEngine::initMaxPages(std::int32_t def) WamrEngine::initMaxPages(std::int32_t def)
{ {
defMaxPages = def; defMaxPages_ = def;
return def; return def;
} }
std::int64_t std::int64_t
WamrEngine::getGas() WamrEngine::getGas()
{ {
return module ? module->getGas() : 0; return moduleWrap_ ? moduleWrap_->getGas() : 0;
} }
wmem wmem
WamrEngine::getMem() const WamrEngine::getMem() const
{ {
return module ? module->getMem() : wmem(); return moduleWrap_ ? moduleWrap_->getMem() : wmem();
} }
InstanceWrapper const& InstanceWrapper const&
WamrEngine::getRT(int m, int i) WamrEngine::getRT(int m, int i)
{ {
if (!module) if (!moduleWrap_)
throw std::runtime_error("WAMR no module"); throw std::runtime_error("no module");
return module->getInstance(i); return moduleWrap_->getInstance(i);
} }
int32_t 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) || if (res.f || !res.r.num_elems || (res.r.data[0].kind != WASM_I32) ||
!res.r.data[0].of.i32) !res.r.data[0].of.i32)
throw std::runtime_error( 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; return res.r.data[0].of.i32;
} }
@@ -988,7 +1049,7 @@ WamrEngine::newTrap(std::string_view txt)
if (!txt.empty()) if (!txt.empty())
wasm_name_new(&msg, txt.size(), txt.data()); wasm_name_new(&msg, txt.size(), txt.data());
return wasm_trap_new(store.get(), &msg); return wasm_trap_new(store_.get(), &msg);
} }
beast::Journal beast::Journal

View File

@@ -28,17 +28,20 @@ namespace ripple {
struct WamrResult struct WamrResult
{ {
wasm_val_vec_t r; wasm_val_vec_t r;
bool f; bool f; // failure flag
WamrResult(unsigned N = 0) : r{0, nullptr, 0, 0, nullptr}, f(false) WamrResult(unsigned N = 0) : r{0, nullptr, 0, 0, nullptr}, f(false)
{ {
if (N) if (N)
wasm_val_vec_new_uninitialized(&r, N); wasm_val_vec_new_uninitialized(&r, N);
} }
~WamrResult() ~WamrResult()
{ {
if (r.size) if (r.size)
wasm_val_vec_delete(&r); wasm_val_vec_delete(&r);
} }
WamrResult(WamrResult const&) = delete; WamrResult(WamrResult const&) = delete;
WamrResult& WamrResult&
operator=(WamrResult const&) = delete; operator=(WamrResult const&) = delete;
@@ -47,6 +50,7 @@ struct WamrResult
{ {
*this = std::move(o); *this = std::move(o);
} }
WamrResult& WamrResult&
operator=(WamrResult&& o) operator=(WamrResult&& o)
{ {
@@ -67,9 +71,9 @@ using FuncInfo = std::pair<wasm_func_t const*, wasm_functype_t const*>;
struct InstanceWrapper struct InstanceWrapper
{ {
wasm_extern_vec_t exports; wasm_extern_vec_t exports_;
InstancePtr mod_inst; InstancePtr instance_;
wasm_exec_env_t exec_env = nullptr; wasm_exec_env_t execEnv_ = nullptr;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink()); beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
private: private:
@@ -116,9 +120,9 @@ public:
struct ModuleWrapper struct ModuleWrapper
{ {
ModulePtr module; ModulePtr module_;
InstanceWrapper mod_inst; InstanceWrapper instanceWrap_;
wasm_exporttype_vec_t export_types; wasm_exporttype_vec_t exportTypes_;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink()); beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
private: private:
@@ -171,12 +175,14 @@ private:
class WamrEngine class WamrEngine
{ {
std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)> engine; std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)> engine_;
std::unique_ptr<wasm_store_t, decltype(&wasm_store_delete)> store; std::unique_ptr<wasm_store_t, decltype(&wasm_store_delete)> store_;
std::unique_ptr<ModuleWrapper> module; std::unique_ptr<ModuleWrapper> moduleWrap_;
std::int32_t defMaxPages = -1; std::int32_t defMaxPages_ = -1;
beast::Journal j_ = beast::Journal(beast::Journal::getNullSink()); beast::Journal j_ = beast::Journal(beast::Journal::getNullSink());
std::mutex m_; // 1 instance mutex
public: public:
WamrEngine(); WamrEngine();
~WamrEngine() = default; ~WamrEngine() = default;
@@ -190,6 +196,14 @@ public:
int64_t gas, int64_t gas,
beast::Journal j); beast::Journal j);
NotTEC
check(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports,
beast::Journal j);
std::int32_t std::int32_t
initMaxPages(std::int32_t def); initMaxPages(std::int32_t def);
@@ -222,6 +236,13 @@ private:
HostFunctions* hfs, HostFunctions* hfs,
int64_t gas); int64_t gas);
NotTEC
checkHlp(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports);
int int
addModule( addModule(
Bytes const& wasmCode, Bytes const& wasmCode,
@@ -247,9 +268,14 @@ private:
std::vector<wasm_val_t> std::vector<wasm_val_t>
convertParams(std::vector<WasmParam> const& params); convertParams(std::vector<WasmParam> const& params);
void static int
compareParamTypes(
wasm_valtype_vec_t const* ftp,
std::vector<wasm_val_t> const& p);
static void
add_param(std::vector<wasm_val_t>& in, int32_t p); add_param(std::vector<wasm_val_t>& in, int32_t p);
void static void
add_param(std::vector<wasm_val_t>& in, int64_t p); add_param(std::vector<wasm_val_t>& in, int64_t p);
template <int NR, class... Types> template <int NR, class... Types>

View File

@@ -29,19 +29,9 @@
namespace ripple { namespace ripple {
Expected<EscrowResult, TER> static std::vector<WasmImportFunc>
runEscrowWasm( createImports(HostFunctions* hfs)
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
HostFunctions* hfs,
int64_t gasLimit,
beast::Journal j)
{ {
// create VM and set cost limit
auto& vm = WasmEngine::instance();
vm.initMaxPages(MAX_PAGES);
std::vector<WasmImportFunc> imports; std::vector<WasmImportFunc> imports;
if (hfs) if (hfs)
@@ -107,11 +97,27 @@ runEscrowWasm(
WASM_IMPORT_FUNC2(imports, traceNum, "trace_num", hfs); WASM_IMPORT_FUNC2(imports, traceNum, "trace_num", hfs);
} }
auto ret = vm.run( return imports;
}
Expected<EscrowResult, TER>
runEscrowWasm(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> 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, wasmCode,
funcName, funcName,
params, params,
imports, createImports(hfs),
hfs, hfs,
gasLimit, gasLimit,
hfs ? hfs->getJournal() : j); hfs ? hfs->getJournal() : j);
@@ -130,6 +136,28 @@ runEscrowWasm(
return EscrowResult{ret->result > 0, ret->cost}; return EscrowResult{ret->result > 0, ret->cost};
} }
NotTEC
preflightEscrowWasm(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> 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<WamrEngine>()) WasmEngine::WasmEngine() : impl(std::make_unique<WamrEngine>())
@@ -156,6 +184,17 @@ WasmEngine::run(
return impl->run(wasmCode, funcName, params, imports, hfs, gasLimit, j); return impl->run(wasmCode, funcName, params, imports, hfs, gasLimit, j);
} }
NotTEC
WasmEngine::check(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports,
beast::Journal j)
{
return impl->check(wasmCode, funcName, params, imports, j);
}
std::int32_t std::int32_t
WasmEngine::initMaxPages(std::int32_t def) WasmEngine::initMaxPages(std::int32_t def)
{ {

View File

@@ -65,6 +65,14 @@ public:
int64_t gasLimit = -1, int64_t gasLimit = -1,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
NotTEC
check(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params = {},
std::vector<WasmImportFunc> const& imports = {},
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
std::int32_t std::int32_t
initMaxPages(std::int32_t def); initMaxPages(std::int32_t def);
@@ -87,4 +95,12 @@ runEscrowWasm(
int64_t gasLimit = -1, int64_t gasLimit = -1,
beast::Journal j = beast::Journal(beast::Journal::getNullSink())); beast::Journal j = beast::Journal(beast::Journal::getNullSink()));
NotTEC
preflightEscrowWasm(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params = {},
HostFunctions* hfs = nullptr,
beast::Journal j = beast::Journal(beast::Journal::getNullSink()));
} // namespace ripple } // namespace ripple

View File

@@ -231,7 +231,14 @@ EscrowCreate::preflight(PreflightContext const& ctx)
<< "EscrowCreate.FinishFunction bad size " << code.size(); << "EscrowCreate.FinishFunction bad size " << code.size();
return temMALFORMED; 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); return preflight2(ctx);