mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
preflight checks for wasm (#5517)
This commit is contained in:
@@ -141,6 +141,8 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temARRAY_TOO_LARGE,
|
||||
temBAD_TRANSFER_FEE,
|
||||
temINVALID_INNER_BATCH,
|
||||
|
||||
temBAD_WASM,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -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<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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user