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 =
86, // the wasm contained a custom section (id=0)
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
// should get a code?
};
@@ -341,8 +342,15 @@ enum ExitType : uint8_t {
ROLLBACK = 2,
ACCEPT = 3,
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 uint8_t max_slots = 255;
const uint8_t max_nonce = 255;

View File

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

View File

@@ -1236,27 +1236,34 @@ hook::apply(
switch (hookApiVersion)
{
case 0: // wasm
case hook_api::CodeType::WASM:
{
HookExecutorWasm executor{hookCtx};
executor.executeWasm(
executor.execute(
bytecode.data(), (size_t)bytecode.size(), isCallback, hookArgument, j);
break;
}
case 1: // js
case hook_api::CodeType::JS:
{
// RHUPTO: populate hookArgument, bytecode, perform execution
HookExecutorJs executor{hookCtx};
HookExecutorJS executor{hookCtx};
executor.execute(
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() << "]: "

View File

@@ -785,60 +785,9 @@ run(int argc, char** argv)
} // 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
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
{
// Work around for https://svn.boost.org/trac/boost/ticket/10657

View File

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

View File

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

View File

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