compiling

This commit is contained in:
Richard Holland
2024-04-22 13:48:36 +10:00
committed by RichardAH
parent 9ab33d580d
commit 71a6f9c4d5
7 changed files with 83 additions and 93 deletions

View File

@@ -224,6 +224,7 @@ enum hook_log_code : uint16_t {
CUSTOM_SECTION_DISALLOWED = CUSTOM_SECTION_DISALLOWED =
86, // the wasm contained a custom section (id=0) 86, // the wasm contained a custom section (id=0)
INTERNAL_ERROR = 87, // an internal error described by the log text INTERNAL_ERROR = 87, // an internal error described by the log text
JS_TEST_FAILURE = 88, // smoke test of js bytecode failed
// RH NOTE: only HookSet msgs got log codes, possibly all Hook log lines // RH NOTE: only HookSet msgs got log codes, possibly all Hook log lines
// should get a code? // should get a code?
}; };
@@ -341,8 +342,15 @@ enum ExitType : uint8_t {
ROLLBACK = 2, ROLLBACK = 2,
ACCEPT = 3, ACCEPT = 3,
JSVM_ERROR = 4, JSVM_ERROR = 4,
LEDGER_ERROR = 5, // if the ledger contained for example a nonsense hookapi number
}; };
enum CodeType : uint8_t {
WASM = 0,
JS = 1,
};
const uint16_t max_state_modifications = 256; const uint16_t max_state_modifications = 256;
const uint8_t max_slots = 255; const uint8_t max_slots = 255;
const uint8_t max_nonce = 255; const uint8_t max_nonce = 255;

View File

@@ -16,6 +16,9 @@
#include <queue> #include <queue>
#include <vector> #include <vector>
#include <wasmedge/wasmedge.h> #include <wasmedge/wasmedge.h>
#include "quickjs.h"
#include "quickjs-libc.h"
namespace hook { namespace hook {
struct HookContext; struct HookContext;
struct HookResult; struct HookResult;
@@ -453,7 +456,7 @@ apply(
bool hasCallback, bool hasCallback,
bool isCallback, bool isCallback,
bool isStrongTSH, bool isStrongTSH,
uint32_t wasmParam, uint32_t hookArgument,
uint8_t hookChainPosition, uint8_t hookChainPosition,
// result of apply() if this is weak exec // result of apply() if this is weak exec
std::shared_ptr<STObject const> const& provisionalMeta); std::shared_ptr<STObject const> const& provisionalMeta);
@@ -501,7 +504,7 @@ struct HookResult
bool isCallback = bool isCallback =
false; // true iff this hook execution is a callback in action false; // true iff this hook execution is a callback in action
bool isStrong = false; bool isStrong = false;
uint32_t wasmParam = 0; uint32_t hookArgument = 0;
uint32_t overrideCount = 0; uint32_t overrideCount = 0;
uint8_t hookChainPosition = 0; uint8_t hookChainPosition = 0;
bool foreignStateSetDisabled = false; bool foreignStateSetDisabled = false;
@@ -511,8 +514,9 @@ struct HookResult
std::shared_ptr<STObject const> provisionalMeta; std::shared_ptr<STObject const> provisionalMeta;
}; };
class HookExecutorWasm; class HookExecutorBase;
class HookExecutorJs; //class HookExecutorWasm;
//class HookExecutorJs;
struct SlotEntry struct SlotEntry
{ {
@@ -550,7 +554,7 @@ struct HookContext
emitFailure; // if this is a callback from a failed emitFailure; // if this is a callback from a failed
// emitted txn then this optional becomes // emitted txn then this optional becomes
// populated with the SLE // populated with the SLE
const HookExecutorWasm* module = 0; const HookExecutorBase* module = 0;
}; };
bool bool
@@ -619,7 +623,7 @@ protected:
public: public:
HookContext& hookCtx; HookContext& hookCtx;
virtual ~HookExecutorBase() {} HookExecutorBase(HookContext& ctx) : hookCtx(ctx) {}
virtual void execute( virtual void execute(
const void* code, const void* code,
@@ -635,6 +639,8 @@ public:
// Base class doesn't implement validation // Base class doesn't implement validation
return "validate() illegally called on HookExecutorBase class"; return "validate() illegally called on HookExecutorBase class";
} }
virtual ~HookExecutorBase() = default;
}; };
@@ -646,9 +652,7 @@ public:
* this is done during execteWasm function. * this is done during execteWasm function.
* The instance is single use. * The instance is single use.
*/ */
class HookExecutorWasm : public HookExecutorBase
{
private:
// create these once at boot and keep them // create these once at boot and keep them
static WasmEdge_String exportName = WasmEdge_StringCreateByCString("env"); static WasmEdge_String exportName = WasmEdge_StringCreateByCString("env");
static WasmEdge_String tableName = WasmEdge_StringCreateByCString("table"); static WasmEdge_String tableName = WasmEdge_StringCreateByCString("table");
@@ -666,6 +670,8 @@ static WasmEdge_String hookFunctionName =
// see: lib/system/allocator.cpp // see: lib/system/allocator.cpp
#define WasmEdge_kPageSize 65536ULL #define WasmEdge_kPageSize 65536ULL
class HookExecutorWasm : public HookExecutorBase
{
public: public:
WasmEdge_ModuleInstanceContext* importObj; WasmEdge_ModuleInstanceContext* importObj;
@@ -747,7 +753,7 @@ public:
const void* wasm, const void* wasm,
size_t len, size_t len,
bool callback, bool callback,
uint32_t wasmParam, uint32_t hookArgument,
beast::Journal const& j) override beast::Journal const& j) override
{ {
// HookExecutorWasm can only execute once // HookExecutorWasm can only execute once
@@ -780,7 +786,7 @@ public:
return; return;
} }
WasmEdge_Value params[1] = {WasmEdge_ValueGenI32((int64_t)wasmParam)}; WasmEdge_Value params[1] = {WasmEdge_ValueGenI32((int64_t)hookArgument)};
WasmEdge_Value returns[1]; WasmEdge_Value returns[1];
res = WasmEdge_VMRunWasmFromBuffer( res = WasmEdge_VMRunWasmFromBuffer(
@@ -808,7 +814,7 @@ public:
} }
HookExecutorWasm(HookContext& ctx) HookExecutorWasm(HookContext& ctx)
: hookCtx(ctx), importObj(WasmEdge_ModuleInstanceCreate(exportName)) : HookExecutorBase(ctx), importObj(WasmEdge_ModuleInstanceCreate(exportName))
{ {
ctx.module = this; ctx.module = this;
@@ -907,7 +913,7 @@ public:
WasmEdge_ModuleInstanceAddMemory(importObj, memName, hostMem); WasmEdge_ModuleInstanceAddMemory(importObj, memName, hostMem);
} }
~HookExecutorWasm() virtual ~HookExecutorWasm()
{ {
WasmEdge_ModuleInstanceDelete(importObj); WasmEdge_ModuleInstanceDelete(importObj);
}; };
@@ -1053,9 +1059,12 @@ public:
* Validate that a web assembly blob can be loaded by wasmedge * Validate that a web assembly blob can be loaded by wasmedge
*/ */
static std::optional<std::string> static std::optional<std::string>
validate(const void* buf, size_t len) validate(const void* buf, size_t buf_len)
{ {
if (buf_len < 5)
return "Could not create QUICKJS instance, bytecode too short.";
std::optional<std::string> retval; std::optional<std::string> retval;
QuickJSVM vm; QuickJSVM vm;
@@ -1065,7 +1074,7 @@ public:
return "Could not create QUICKJS instance"; return "Could not create QUICKJS instance";
JSValue obj = JSValue obj =
JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); JS_ReadObject(ctx, (uint8_t const*)buf, buf_len, JS_READ_OBJ_BYTECODE);
if (JS_IsException(obj)) if (JS_IsException(obj))
{ {
if (const char* str = JS_ToCString(ctx, obj); str) if (const char* str = JS_ToCString(ctx, obj); str)
@@ -1074,7 +1083,7 @@ public:
JS_FreeCString(ctx, str); JS_FreeCString(ctx, str);
} }
JS_FreeVal(ctx, obj); JS_FreeValue(ctx, obj);
return retval; return retval;
} }
@@ -1127,7 +1136,7 @@ public:
const void* buf, const void* buf,
size_t buf_len, size_t buf_len,
bool callback, bool callback,
uint32_t callParam, uint32_t hookArgument,
beast::Journal const& j) override beast::Journal const& j) override
{ {
// HookExecutorWasm can only execute once // HookExecutorWasm can only execute once
@@ -1138,6 +1147,7 @@ public:
<< "]: creating quickjs instance"; << "]: creating quickjs instance";
QuickJSVM vm; QuickJSVM vm;
JSContext* ctx = vm.ctx;
if (!vm.sane()) if (!vm.sane())
{ {
@@ -1149,11 +1159,11 @@ public:
} }
JSValue obj = JSValue obj =
JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); JS_ReadObject(ctx, (uint8_t const*)buf, buf_len, JS_READ_OBJ_BYTECODE);
if (JS_IsException(obj)) if (JS_IsException(obj))
{ {
JS_FreeVal(ctx, obj); JS_FreeValue(ctx, obj);
JLOG(j.warn()) << "HookError[" << HC_ACC() JLOG(j.warn()) << "HookError[" << HC_ACC()
<< "]: Could not create QUICKJS instance (invalid bytecode)."; << "]: Could not create QUICKJS instance (invalid bytecode).";
hookCtx.result.exitType = hook_api::ExitType::JSVM_ERROR; hookCtx.result.exitType = hook_api::ExitType::JSVM_ERROR;
@@ -1179,11 +1189,11 @@ public:
char expr[256]; char expr[256];
int expr_len = int expr_len =
snprintf(expr_len, 256, "%s(%d)", snprintf(expr, 256, "%s(%d)",
callback ? "Callback" : "Hook", callback ? "Callback" : "Hook",
callParam); hookArgument);
if (char_count < 7 || char_count == 256) if (expr_len < 7 || expr_len == 256)
{ {
JLOG(j.warn()) << "HookError[" << HC_ACC() JLOG(j.warn()) << "HookError[" << HC_ACC()
<< "]: Could not create QUICKJS instance (expr string)."; << "]: Could not create QUICKJS instance (expr string).";
@@ -1192,7 +1202,7 @@ public:
return; return;
} }
JSValue val = val =
JS_Eval(vm.ctx, expr, expr_len, "<qjsvm>", 0); JS_Eval(vm.ctx, expr, expr_len, "<qjsvm>", 0);
if (JS_IsException(val)) if (JS_IsException(val))
@@ -1214,14 +1224,12 @@ public:
} }
HookExecutorJS(HookContext& ctx) HookExecutorJS(HookContext& ctx)
: hookCtx(ctx) : HookExecutorBase(ctx)
{ {
ctx.module = this; ctx.module = this;
} }
~HookExecutorJS() virtual ~HookExecutorJS()
{ {
}; };
}; };

View File

@@ -1236,27 +1236,34 @@ hook::apply(
switch (hookApiVersion) switch (hookApiVersion)
{ {
case 0: // wasm case hook_api::CodeType::WASM:
{ {
HookExecutorWasm executor{hookCtx}; HookExecutorWasm executor{hookCtx};
executor.executeWasm( executor.execute(
bytecode.data(), (size_t)bytecode.size(), isCallback, hookArgument, j); bytecode.data(), (size_t)bytecode.size(), isCallback, hookArgument, j);
break;
} }
case 1: // js case hook_api::CodeType::JS:
{ {
// RHUPTO: populate hookArgument, bytecode, perform execution // RHUPTO: populate hookArgument, bytecode, perform execution
HookExecutorJs executor{hookCtx}; HookExecutorJS executor{hookCtx};
executor.execute( executor.execute(
bytecode.data(), (size_t)bytecode.size(), isCallback, hookArgument, j); bytecode.data(), (size_t)bytecode.size(), isCallback, hookArgument, j);
js_std_eval_binary_bare(ctx, wasm.data(), wasm.size()); break;
} }
default:
{
hookCtx.result.exitType = hook_api::ExitType::LEDGER_ERROR;
return hookCtx.result;
}
} }
JLOG(j.trace()) << "HookInfo[" << HC_ACC() << "]: " JLOG(j.trace()) << "HookInfo[" << HC_ACC() << "]: "

View File

@@ -785,60 +785,9 @@ run(int argc, char** argv)
} // namespace ripple } // namespace ripple
#include "quickjs-libc.h"
static JSContext*
JS_NewCustomContext(JSRuntime* rt)
{
JSContext* ctx = JS_NewContextRaw(rt);
if (!ctx)
return NULL;
JS_AddIntrinsicBaseObjects(ctx);
JS_AddIntrinsicDate(ctx);
JS_AddIntrinsicEval(ctx);
JS_AddIntrinsicStringNormalize(ctx);
JS_AddIntrinsicRegExp(ctx);
JS_AddIntrinsicJSON(ctx);
JS_AddIntrinsicProxy(ctx);
JS_AddIntrinsicMapSet(ctx);
JS_AddIntrinsicTypedArrays(ctx);
JS_AddIntrinsicPromise(ctx);
JS_AddIntrinsicBigInt(ctx);
return ctx;
}
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
const uint32_t qjsc_x_size = 140;
const uint8_t qjsc_x[140] = {
0x43, 0x05, 0x02, 0x78, 0x08, 0x78, 0x2e, 0x6a, 0x73, 0x02, 0x69, 0x0e,
0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x67, 0x0c,
0x00, 0x06, 0x00, 0xa2, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x16,
0x01, 0xa4, 0x01, 0x00, 0x00, 0x00, 0x3f, 0xe3, 0x00, 0x00, 0x00, 0x40,
0xc2, 0x00, 0x40, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x38, 0xe3, 0x00, 0x00,
0x00, 0xf0, 0xcf, 0x28, 0xc8, 0x03, 0x01, 0x04, 0x1f, 0x00, 0x08, 0x0e,
0x0c, 0x43, 0x06, 0x00, 0xc6, 0x03, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00,
0x2a, 0x01, 0xca, 0x03, 0x02, 0x00, 0x20, 0x61, 0x00, 0x00, 0xb7, 0xcb,
0x62, 0x00, 0x00, 0xbf, 0x0a, 0xa4, 0xec, 0x1d, 0x38, 0xe6, 0x00, 0x00,
0x00, 0x42, 0xe7, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x24, 0x01, 0x00,
0x0e, 0x62, 0x00, 0x00, 0x90, 0x11, 0x63, 0x00, 0x00, 0x0e, 0xee, 0xdd,
0x29, 0xc8, 0x03, 0x01, 0x03, 0x04, 0x44, 0x8f,
};
JSRuntime* rt;
JSContext* ctx;
rt = JS_NewRuntime();
JS_SetMaxStackSize(rt, 65535);
JS_SetMemoryLimit(rt, 16 * 1024 * 1024);
ctx = JS_NewContextRaw(rt);
JS_AddIntrinsicBaseObjects(ctx);
JS_AddIntrinsicJSON(ctx);
JS_AddIntrinsicBigInt(ctx);
js_std_add_helpers(ctx, argc, argv);
js_std_eval_binary(ctx, qjsc_x, qjsc_x_size, 0);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
{ {
// Work around for https://svn.boost.org/trac/boost/ticket/10657 // Work around for https://svn.boost.org/trac/boost/ticket/10657

View File

@@ -611,7 +611,7 @@ Change::activateXahauGenesis()
} }
std::optional<std::string> result2 = std::optional<std::string> result2 =
hook::HookExecutor::validateWasm( hook::HookExecutorWasm::validate(
wasmBytes.data(), (size_t)wasmBytes.size()); wasmBytes.data(), (size_t)wasmBytes.size());
if (result2) if (result2)

View File

@@ -461,15 +461,26 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
if (version == 1) if (version == 1)
{ {
// RHTODO: don't eval here, scan for valid bytecode, return error if not // RHTODO: guard or other check for js, depending on design choices
// figure out how to correctly sandbox quickjs runtime so segfault doesnt crash
// std::optional<std::string> result =
// for now, do nothing here hook::HookExecutorJS::validate(
// hook.data(), (size_t)hook.size());
if (result)
{
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::JS_TEST_FAILURE << ")["
<< HS_ACC()
<< "Tried to set a hook with invalid code. VM error: "
<< *result;
return false;
}
return true; return true;
} }
else if (version == 0)
if (version == 0)
{ {
// RH NOTE: validateGuards has a generic non-rippled specific // RH NOTE: validateGuards has a generic non-rippled specific
// interface so it can be used in other projects (i.e. tooling). // interface so it can be used in other projects (i.e. tooling).
@@ -534,7 +545,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
<< "size = " << hook.size(); << "size = " << hook.size();
std::optional<std::string> result2 = std::optional<std::string> result2 =
hook::HookExecutor::validateWasm( hook::HookExecutorWasm::validate(
hook.data(), (size_t)hook.size()); hook.data(), (size_t)hook.size());
if (result2) if (result2)
@@ -549,6 +560,11 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
return *result; return *result;
} }
JLOG(ctx.j.trace())
<< "HookSet(" << hook::log::HASH_OR_CODE << ")[" << HS_ACC()
<< "]: Malformed transaction: SetHook specified invalid HookApiVersion.";
return false;
} }
case hsoINVALID: case hsoINVALID:

View File

@@ -1242,7 +1242,7 @@ Transactor::executeHookChain(
bool hasCallback = hookDef->isFieldPresent(sfHookCallbackFee); bool hasCallback = hookDef->isFieldPresent(sfHookCallbackFee);
uint16_t hookApiVersion = hookDef-getFieldU16(sfHookApiVersion); uint16_t hookApiVersion = hookDef->getFieldU16(sfHookApiVersion);
try try
{ {
@@ -1397,6 +1397,7 @@ Transactor::doHookCallback(
hook::HookResult callbackResult = hook::apply( hook::HookResult callbackResult = hook::apply(
hookDef->getFieldH256(sfHookSetTxnID), hookDef->getFieldH256(sfHookSetTxnID),
callbackHookHash, callbackHookHash,
hookDef->getFieldU16(sfHookApiVersion),
ns, ns,
hookDef->getFieldVL(sfCreateCode), hookDef->getFieldVL(sfCreateCode),
parameters, parameters,
@@ -1662,6 +1663,7 @@ Transactor::doAgainAsWeak(
hook::HookResult aawResult = hook::apply( hook::HookResult aawResult = hook::apply(
hookDef->getFieldH256(sfHookSetTxnID), hookDef->getFieldH256(sfHookSetTxnID),
hookHash, hookHash,
hookDef->getFieldU16(sfHookApiVersion),
ns, ns,
hookDef->getFieldVL(sfCreateCode), hookDef->getFieldVL(sfCreateCode),
parameters, parameters,