mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-29 01:50:13 +00:00
first iteration of adding an instruction limit to jshooks
This commit is contained in:
@@ -3340,7 +3340,7 @@ static void *worker_func(void *opaque)
|
||||
JSContext *ctx;
|
||||
JSValue val;
|
||||
|
||||
rt = JS_NewRuntime();
|
||||
rt = JS_NewRuntime(-1);
|
||||
if (rt == NULL) {
|
||||
fprintf(stderr, "JS_NewRuntime failure");
|
||||
exit(1);
|
||||
|
||||
@@ -303,6 +303,7 @@ struct JSRuntime {
|
||||
uint32_t operator_count;
|
||||
#endif
|
||||
void *user_opaque;
|
||||
int64_t instruction_limit;
|
||||
};
|
||||
|
||||
struct JSClass {
|
||||
@@ -1615,7 +1616,7 @@ static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
|
||||
}
|
||||
#endif
|
||||
|
||||
JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
|
||||
JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque, uint32_t instructionLimit)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSMallocState ms;
|
||||
@@ -1635,6 +1636,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
|
||||
}
|
||||
rt->malloc_state = ms;
|
||||
rt->malloc_gc_threshold = 256 * 1024;
|
||||
rt->instruction_limit = instructionLimit;
|
||||
|
||||
bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
|
||||
set_dummy_numeric_ops(&rt->bigint_ops);
|
||||
@@ -1772,9 +1774,9 @@ static const JSMallocFunctions def_malloc_funcs = {
|
||||
js_def_malloc_usable_size,
|
||||
};
|
||||
|
||||
JSRuntime *JS_NewRuntime(void)
|
||||
JSRuntime *JS_NewRuntime(uint32_t instructionLimit)
|
||||
{
|
||||
return JS_NewRuntime2(&def_malloc_funcs, NULL);
|
||||
return JS_NewRuntime2(&def_malloc_funcs, NULL, instructionLimit);
|
||||
}
|
||||
|
||||
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
|
||||
@@ -6857,6 +6859,15 @@ static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
|
||||
|
||||
static inline __exception int js_poll_interrupts(JSContext *ctx)
|
||||
{
|
||||
if (unlikely(--ctx->rt->instruction_limit <= 0))
|
||||
{
|
||||
/* XXX: should set a specific flag to avoid catching */
|
||||
/* RHTODO: investigate if this is user-catchable. */
|
||||
JS_ThrowInternalError(ctx, "interrupted");
|
||||
JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (unlikely(--ctx->interrupt_counter <= 0)) {
|
||||
return __js_poll_interrupts(ctx);
|
||||
} else {
|
||||
|
||||
@@ -331,7 +331,7 @@ typedef struct JSMallocFunctions {
|
||||
|
||||
typedef struct JSGCObjectHeader JSGCObjectHeader;
|
||||
|
||||
JSRuntime *JS_NewRuntime(void);
|
||||
JSRuntime *JS_NewRuntime(uint32_t instructionLimit);
|
||||
/* info lifetime must exceed that of rt */
|
||||
void JS_SetRuntimeInfo(JSRuntime *rt, const char *info);
|
||||
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit);
|
||||
@@ -341,7 +341,7 @@ void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size);
|
||||
/* should be called when changing thread to update the stack top value
|
||||
used to check stack overflow. */
|
||||
void JS_UpdateStackTop(JSRuntime *rt);
|
||||
JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque);
|
||||
JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque, uint32_t instructionLimit);
|
||||
void JS_FreeRuntime(JSRuntime *rt);
|
||||
void *JS_GetRuntimeOpaque(JSRuntime *rt);
|
||||
void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque);
|
||||
|
||||
@@ -225,6 +225,8 @@ enum hook_log_code : uint16_t {
|
||||
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
|
||||
JS_FEE_MISSING = 89,
|
||||
JS_FEE_TOO_HIGH = 90,
|
||||
// RH NOTE: only HookSet msgs got log codes, possibly all Hook log lines
|
||||
// should get a code?
|
||||
};
|
||||
|
||||
@@ -819,7 +819,8 @@ apply(
|
||||
uint32_t hookArgument,
|
||||
uint8_t hookChainPosition,
|
||||
// result of apply() if this is weak exec
|
||||
std::shared_ptr<STObject const> const& provisionalMeta);
|
||||
std::shared_ptr<STObject const> const& provisionalMeta,
|
||||
uint32_t instructionLimit);
|
||||
|
||||
struct HookContext;
|
||||
|
||||
@@ -998,6 +999,7 @@ public:
|
||||
size_t len,
|
||||
bool callback,
|
||||
uint32_t param,
|
||||
uint32_t instructionLimit,
|
||||
beast::Journal const& j) = 0;
|
||||
|
||||
static std::optional<std::string> validate(
|
||||
@@ -1122,6 +1124,7 @@ public:
|
||||
size_t len,
|
||||
bool callback,
|
||||
uint32_t hookArgument,
|
||||
uint32_t instructionLimit, /* this is unused in wasm, set to 0 */
|
||||
beast::Journal const& j) override
|
||||
{
|
||||
// HookExecutorWasm can only execute once
|
||||
@@ -1298,9 +1301,9 @@ public:
|
||||
JSRuntime* rt = NULL;
|
||||
JSContext* ctx = NULL;
|
||||
|
||||
QuickJSVM(void* hookCtx)
|
||||
QuickJSVM(void* hookCtx, uint32_t instructionLimit)
|
||||
{
|
||||
rt = JS_NewRuntime();
|
||||
rt = JS_NewRuntime(instructionLimit);
|
||||
ctx = JS_NewContextRaw(rt);
|
||||
JS_AddIntrinsicBaseObjects(ctx);
|
||||
JS_AddIntrinsicEval(ctx);
|
||||
@@ -1430,7 +1433,7 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate that a web assembly blob can be loaded by wasmedge
|
||||
* Validate that a js blob can be loaded by quickjs
|
||||
*/
|
||||
static std::optional<std::string>
|
||||
validate(const void* buf, size_t buf_len)
|
||||
@@ -1441,7 +1444,8 @@ public:
|
||||
|
||||
std::optional<std::string> retval;
|
||||
|
||||
QuickJSVM vm{NULL};
|
||||
// RHNOTE: hard instrction (internal program counter loop) limit of 1MM, like wasm.
|
||||
QuickJSVM vm{NULL, 1000000};
|
||||
JSContext* ctx = vm.ctx;
|
||||
|
||||
if (!vm.sane())
|
||||
@@ -1511,6 +1515,7 @@ public:
|
||||
size_t buf_len,
|
||||
bool callback,
|
||||
uint32_t hookArgument,
|
||||
uint32_t instructionLimit,
|
||||
beast::Journal const& j) override
|
||||
{
|
||||
// HookExecutorWasm can only execute once
|
||||
@@ -1520,7 +1525,7 @@ public:
|
||||
JLOG(j.trace()) << "HookInfo[" << HC_ACC()
|
||||
<< "]: creating quickjs instance";
|
||||
|
||||
QuickJSVM vm{reinterpret_cast<void*>(&hookCtx)};
|
||||
QuickJSVM vm{reinterpret_cast<void*>(&hookCtx), instructionLimit};
|
||||
JSContext* ctx = vm.ctx;
|
||||
|
||||
if (!vm.sane())
|
||||
|
||||
@@ -1535,7 +1535,8 @@ hook::apply(
|
||||
bool isStrong,
|
||||
uint32_t hookArgument,
|
||||
uint8_t hookChainPosition,
|
||||
std::shared_ptr<STObject const> const& provisionalMeta)
|
||||
std::shared_ptr<STObject const> const& provisionalMeta,
|
||||
uint32_t instructionLimit)
|
||||
{
|
||||
HookContext hookCtx = {
|
||||
.applyCtx = applyCtx,
|
||||
@@ -1582,7 +1583,8 @@ hook::apply(
|
||||
HookExecutorWasm executor{hookCtx};
|
||||
|
||||
executor.execute(
|
||||
bytecode.data(), (size_t)bytecode.size(), isCallback, hookArgument, j);
|
||||
bytecode.data(),
|
||||
(size_t)bytecode.size(), isCallback, hookArgument, 0 /* instructioin limit not used in wasm */, j);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1594,7 +1596,7 @@ hook::apply(
|
||||
HookExecutorJS executor{hookCtx};
|
||||
|
||||
executor.execute(
|
||||
bytecode.data(), (size_t)bytecode.size(), isCallback, hookArgument, j);
|
||||
bytecode.data(), (size_t)bytecode.size(), isCallback, hookArgument, instructionLimit, j);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
auto version = hookSetObj.getFieldU16(sfHookApiVersion);
|
||||
if (version > 1)
|
||||
{
|
||||
// we currently only accept api version 0
|
||||
// we currently only accept api version 0 and 1
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << hook::log::API_INVALID << ")[" << HS_ACC()
|
||||
<< "]: Malformed transaction: SetHook "
|
||||
@@ -462,7 +462,33 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
if (version == 1)
|
||||
{
|
||||
// RHTODO: guard or other check for js, depending on design choices
|
||||
|
||||
|
||||
if (hookSetObj.isFieldPresent(sfFee) &&
|
||||
isXRP(hookSetObj.getFieldAmount(sfFee)))
|
||||
{
|
||||
STAmount amt = hookSetObj.getFieldAmount(sfFee);
|
||||
uint64_t fee = amt.xrp().drops();
|
||||
if (amt < beast::zero || fee < 1 || fee > 1000000)
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << hook::log::JS_FEE_TOO_HIGH << ")["
|
||||
<< HS_ACC()
|
||||
<< "]: Malformed transaction: When creating a JS Hook "
|
||||
<< "you must include a Fee <= 1000000.";
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << hook::log::JS_FEE_MISSING << ")["
|
||||
<< HS_ACC()
|
||||
<< "]: Malformed transaction: When creating a JS Hook "
|
||||
<< "you must include a Fee field indicating the instruction limit.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::string> result =
|
||||
hook::HookExecutorJS::validate(
|
||||
hook.data(), (size_t)hook.size());
|
||||
@@ -749,7 +775,8 @@ SetHook::preflight(PreflightContext const& ctx)
|
||||
if (name != sfCreateCode && name != sfHookHash &&
|
||||
name != sfHookNamespace && name != sfHookParameters &&
|
||||
name != sfHookOn && name != sfHookGrants &&
|
||||
name != sfHookApiVersion && name != sfFlags)
|
||||
name != sfHookApiVersion && name != sfFlags &&
|
||||
name != sfFee)
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "HookSet(" << hook::log::HOOK_INVALID_FIELD << ")["
|
||||
@@ -1525,6 +1552,17 @@ SetHook::setHook()
|
||||
return tecREQUIRES_FLAG;
|
||||
}
|
||||
|
||||
uint16_t hookApiVersion = hookSetObj->get().getFieldU16(sfHookApiVersion);
|
||||
|
||||
if (hookApiVersion == 1 && !hookSetObj->get().isFieldPresent(sfFee))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "HookSet(" << hook::log::JS_FEE_MISSING << ")["
|
||||
<< HS_ACC()
|
||||
<< "]: Malformed transaction: SetHook operation for JS Hook missing fee.";
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
ripple::Blob wasmBytes =
|
||||
hookSetObj->get().getFieldVL(sfCreateCode);
|
||||
|
||||
@@ -1611,6 +1649,14 @@ SetHook::setHook()
|
||||
slesToUpdate.emplace(*oldDefKeylet, oldDefSLE);
|
||||
}
|
||||
|
||||
// override instruction count with fee for js
|
||||
if (hookApiVersion == 1)
|
||||
{
|
||||
uint64_t fee = hookSetObj->get().getFieldAmount(sfFee).xrp().drops();
|
||||
maxInstrCountHook = fee;
|
||||
maxInstrCountCbak = fee; // RH TODO: add a second fee for cbak?
|
||||
}
|
||||
|
||||
auto newHookDef = std::make_shared<SLE>(keylet);
|
||||
newHookDef->setFieldH256(sfHookHash, *createHookHash);
|
||||
newHookDef->setFieldH256(sfHookOn, *newHookOn);
|
||||
@@ -1620,9 +1666,7 @@ SetHook::setHook()
|
||||
hookSetObj->get().isFieldPresent(sfHookParameters)
|
||||
? hookSetObj->get().getFieldArray(sfHookParameters)
|
||||
: STArray{});
|
||||
newHookDef->setFieldU16(
|
||||
sfHookApiVersion,
|
||||
hookSetObj->get().getFieldU16(sfHookApiVersion));
|
||||
newHookDef->setFieldU16(sfHookApiVersion, hookApiVersion);
|
||||
newHookDef->setFieldVL(sfCreateCode, wasmBytes);
|
||||
newHookDef->setFieldH256(
|
||||
sfHookSetTxnID, ctx.tx.getTransactionID());
|
||||
|
||||
@@ -1244,6 +1244,8 @@ Transactor::executeHookChain(
|
||||
|
||||
uint16_t hookApiVersion = hookDef->getFieldU16(sfHookApiVersion);
|
||||
|
||||
uint32_t fee = (uint32_t)(hookDef->getFieldAmount(sfFee).xrp().drops());
|
||||
|
||||
try
|
||||
{
|
||||
results.push_back(hook::apply(
|
||||
@@ -1262,7 +1264,8 @@ Transactor::executeHookChain(
|
||||
strong,
|
||||
(strong ? 0 : 1UL), // 0 = strong, 1 = weak
|
||||
hook_no - 1,
|
||||
provisionalMeta));
|
||||
provisionalMeta,
|
||||
fee));
|
||||
|
||||
executedHookCount_++;
|
||||
|
||||
@@ -1379,6 +1382,8 @@ Transactor::doHookCallback(
|
||||
? hookObj.getFieldH256(sfHookNamespace)
|
||||
: hookDef->getFieldH256(sfHookNamespace));
|
||||
|
||||
uint64_t instructionLimit = hookDef->getFieldAmount(sfFee).xrp().drops();
|
||||
|
||||
std::map<std::vector<uint8_t>, std::vector<uint8_t>> parameters;
|
||||
if (hook::gatherHookParameters(hookDef, hookObj, parameters, j_))
|
||||
{
|
||||
@@ -1413,7 +1418,8 @@ Transactor::doHookCallback(
|
||||
? 1UL
|
||||
: 0UL,
|
||||
hook_no - 1,
|
||||
provisionalMeta);
|
||||
provisionalMeta,
|
||||
instructionLimit);
|
||||
|
||||
executedHookCount_++;
|
||||
|
||||
@@ -1658,6 +1664,8 @@ Transactor::doAgainAsWeak(
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t instructionLimit = (uint32_t)(hookDef->getFieldAmount(sfFee).xrp().drops());
|
||||
|
||||
try
|
||||
{
|
||||
hook::HookResult aawResult = hook::apply(
|
||||
@@ -1676,7 +1684,8 @@ Transactor::doAgainAsWeak(
|
||||
false,
|
||||
2UL, // param 2 = aaw
|
||||
hook_no - 1,
|
||||
provisionalMeta);
|
||||
provisionalMeta,
|
||||
instructionLimit);
|
||||
|
||||
executedHookCount_++;
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
{sfHookParameters, soeOPTIONAL},
|
||||
{sfHookOn, soeOPTIONAL},
|
||||
{sfHookApiVersion, soeOPTIONAL},
|
||||
{sfFee, soeOPTIONAL},
|
||||
{sfFlags, soeOPTIONAL}});
|
||||
|
||||
add(sfHookGrant.jsonName.c_str(),
|
||||
|
||||
Reference in New Issue
Block a user