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,
temBAD_TRANSFER_FEE,
temINVALID_INNER_BATCH,
temBAD_WASM,
};
//------------------------------------------------------------------------------

View File

@@ -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."),

View File

@@ -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

View File

@@ -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<uint8_t> wasm(wasmStr.begin(), wasmStr.end());
std::string funcName("mock_escrow");
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
@@ -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);
}
{

View File

@@ -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;
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<bool>(mod_inst);
return static_cast<bool>(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<wasm_externtype_t*>(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<std::uint8_t*>(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<WasmImportFunc> 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<WasmImportFunc> 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<wasm_importtype_vec_t, decltype(&wasm_importtype_vec_delete)>
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<WasmImportFunc> 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<ModuleWrapper>(
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<ModuleWrapper>(
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<wasm_val_t>
@@ -699,6 +688,8 @@ WamrEngine::convertParams(std::vector<WasmParam> 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<WasmParam> const& params)
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
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); //
// 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<TER>(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<decltype(m)> lg(m);
std::lock_guard<decltype(m_)> 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<TER>(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<TER>(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<TER>(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<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;
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
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

View File

@@ -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<wasm_func_t const*, wasm_functype_t const*>;
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<wasm_engine_t, decltype(&wasm_engine_delete)> engine;
std::unique_ptr<wasm_store_t, decltype(&wasm_store_delete)> store;
std::unique_ptr<ModuleWrapper> module;
std::int32_t defMaxPages = -1;
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<ModuleWrapper> 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<WasmParam> const& params,
std::vector<WasmImportFunc> 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<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports);
int
addModule(
Bytes const& wasmCode,
@@ -247,9 +268,14 @@ private:
std::vector<wasm_val_t>
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);
void
static void
add_param(std::vector<wasm_val_t>& in, int64_t p);
template <int NR, class... Types>

View File

@@ -29,19 +29,9 @@
namespace ripple {
Expected<EscrowResult, TER>
runEscrowWasm(
Bytes const& wasmCode,
std::string_view funcName,
std::vector<WasmParam> const& params,
HostFunctions* hfs,
int64_t gasLimit,
beast::Journal j)
static std::vector<WasmImportFunc>
createImports(HostFunctions* hfs)
{
// create VM and set cost limit
auto& vm = WasmEngine::instance();
vm.initMaxPages(MAX_PAGES);
std::vector<WasmImportFunc> imports;
if (hfs)
@@ -107,11 +97,27 @@ runEscrowWasm(
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,
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<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>())
@@ -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<WasmParam> const& params,
std::vector<WasmImportFunc> const& imports,
beast::Journal j)
{
return impl->check(wasmCode, funcName, params, imports, j);
}
std::int32_t
WasmEngine::initMaxPages(std::int32_t def)
{

View File

@@ -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<WasmParam> const& params = {},
std::vector<WasmImportFunc> 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<WasmParam> const& params = {},
HostFunctions* hfs = nullptr,
beast::Journal j = beast::Journal(beast::Journal::getNullSink()));
} // namespace ripple

View File

@@ -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);