mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-19 10:05:48 +00:00
324 lines
19 KiB
C++
324 lines
19 KiB
C++
/**
|
|
* RH NOTE:
|
|
* This file contains macros for converting the hook api definitions into the
|
|
* currently used wasm runtime. Web assembly runtimes are more or less fungible,
|
|
* and at time of writing hooks has moved to WasmEdge from SSVM and before that
|
|
* from wasmer. After the first move it was decided there should be a relatively
|
|
* static interface for the definition and programming of the hook api itself,
|
|
* with the runtime-specific behaviour hidden away by templates or macros.
|
|
* Macros are more expressive and can themselves include templates so macros
|
|
* were then used.
|
|
*/
|
|
|
|
#define LPAREN (
|
|
#define RPAREN )
|
|
#define COMMA ,
|
|
#define EXPAND(...) __VA_ARGS__
|
|
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
|
|
#define CAT2(L, R) CAT2_(L, R)
|
|
#define CAT2_(L, R) L##R
|
|
#define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
|
|
#define EMPTY()
|
|
#define DEFER(id) id EMPTY()
|
|
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
|
|
#define VA_NARGS_IMPL( \
|
|
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, N, ...) \
|
|
N
|
|
#define VA_NARGS(__drop, ...) \
|
|
VA_NARGS_IMPL(__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
|
|
#define FIRST(a, b) a
|
|
#define SECOND(a, b) b
|
|
#define STRIP_TYPES(...) FOR_VARS(SECOND, 0, __VA_ARGS__)
|
|
|
|
#define DELIM_0 ,
|
|
#define DELIM_1
|
|
#define DELIM_2 ;
|
|
#define DELIM(S) DELIM_##S
|
|
|
|
#define FOR_VAR_1(T, S, D) SEP(T, D)
|
|
#define FOR_VAR_2(T, S, a, b) FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_1(T, S, b)
|
|
#define FOR_VAR_3(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_2(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_4(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_3(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_5(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_4(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_6(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_5(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_7(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_6(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_8(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_7(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_9(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_8(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_10(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_9(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_11(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_10(T, S, __VA_ARGS__)
|
|
#define FOR_VAR_12(T, S, a, ...) \
|
|
FOR_VAR_1(T, S, a) DELIM(S) FOR_VAR_11(T, S, __VA_ARGS__)
|
|
#define FOR_VARS(T, S, ...) \
|
|
DEFER(CAT(FOR_VAR_, VA_NARGS(NULL, __VA_ARGS__)) \
|
|
CAT(LPAREN T COMMA S COMMA OBSTRUCT(__VA_ARGS__) RPAREN))
|
|
|
|
#define SEP(OP, D) EXPAND(OP CAT2(SEP_, D) RPAREN)
|
|
#define SEP_uint32_t LPAREN uint32_t COMMA
|
|
#define SEP_int32_t LPAREN int32_t COMMA
|
|
#define SEP_uint64_t LPAREN uint64_t COMMA
|
|
#define SEP_int64_t LPAREN int64_t COMMA
|
|
|
|
#define VAL_uint32_t WasmEdge_ValueGetI32(in[_stack++])
|
|
#define VAL_int32_t WasmEdge_ValueGetI32(in[_stack++])
|
|
#define VAL_uint64_t WasmEdge_ValueGetI64(in[_stack++])
|
|
#define VAL_int64_t WasmEdge_ValueGetI64(in[_stack++])
|
|
|
|
#define VAR_ASSIGN(T, V) T V = CAT(VAL_##T)
|
|
|
|
#define RET_uint32_t(return_code) WasmEdge_ValueGenI32(return_code)
|
|
#define RET_int32_t(return_code) WasmEdge_ValueGenI32(return_code)
|
|
#define RET_uint64_t(return_code) WasmEdge_ValueGenI64(return_code)
|
|
#define RET_int64_t(return_code) WasmEdge_ValueGenI64(return_code)
|
|
|
|
#define RET_ASSIGN(T, return_code) CAT2(RET_, T(return_code))
|
|
|
|
#define TYP_uint32_t WasmEdge_ValType_I32
|
|
#define TYP_int32_t WasmEdge_ValType_I32
|
|
#define TYP_uint64_t WasmEdge_ValType_I64
|
|
#define TYP_int64_t WasmEdge_ValType_I64
|
|
|
|
#define WASM_VAL_TYPE(T, b) CAT2(TYP_, T)
|
|
|
|
#define DECLARE_HOOK_FUNCTION(R, F, ...) \
|
|
R F(hook::HookContext& hookCtx, \
|
|
WasmEdge_CallingFrameContext const& frameCtx, \
|
|
__VA_ARGS__); \
|
|
extern WasmEdge_Result WasmFunction##F( \
|
|
void* data_ptr, \
|
|
const WasmEdge_CallingFrameContext* frameCtx, \
|
|
const WasmEdge_Value* in, \
|
|
WasmEdge_Value* out); \
|
|
extern WasmEdge_ValType WasmFunctionParams##F[]; \
|
|
extern WasmEdge_ValType WasmFunctionResult##F[]; \
|
|
extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \
|
|
extern WasmEdge_String WasmFunctionName##F;
|
|
|
|
#define DECLARE_HOOK_FUNCNARG(R, F) \
|
|
R F(hook::HookContext& hookCtx, \
|
|
WasmEdge_CallingFrameContext const& frameCtx); \
|
|
extern WasmEdge_Result WasmFunction##F( \
|
|
void* data_ptr, \
|
|
const WasmEdge_CallingFrameContext* frameCtx, \
|
|
const WasmEdge_Value* in, \
|
|
WasmEdge_Value* out); \
|
|
extern WasmEdge_ValType WasmFunctionResult##F[]; \
|
|
extern WasmEdge_FunctionTypeContext* WasmFunctionType##F; \
|
|
extern WasmEdge_String WasmFunctionName##F;
|
|
|
|
#define DEFINE_HOOK_FUNCTION(R, F, ...) \
|
|
WasmEdge_Result hook_api::WasmFunction##F( \
|
|
void* data_ptr, \
|
|
const WasmEdge_CallingFrameContext* frameCtx, \
|
|
const WasmEdge_Value* in, \
|
|
WasmEdge_Value* out) \
|
|
{ \
|
|
int _stack = 0; \
|
|
FOR_VARS(VAR_ASSIGN, 2, __VA_ARGS__); \
|
|
hook::HookContext* hookCtx = \
|
|
reinterpret_cast<hook::HookContext*>(data_ptr); \
|
|
R return_code = hook_api::F( \
|
|
*hookCtx, \
|
|
*const_cast<WasmEdge_CallingFrameContext*>(frameCtx), \
|
|
STRIP_TYPES(__VA_ARGS__)); \
|
|
if (return_code == RC_ROLLBACK || return_code == RC_ACCEPT) \
|
|
return WasmEdge_Result_Terminate; \
|
|
out[0] = RET_ASSIGN(R, return_code); \
|
|
return WasmEdge_Result_Success; \
|
|
}; \
|
|
WasmEdge_ValType hook_api::WasmFunctionParams##F[] = { \
|
|
FOR_VARS(WASM_VAL_TYPE, 0, __VA_ARGS__)}; \
|
|
WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \
|
|
WASM_VAL_TYPE(R, dummy)}; \
|
|
WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \
|
|
WasmEdge_FunctionTypeCreate( \
|
|
WasmFunctionParams##F, \
|
|
VA_NARGS(NULL, __VA_ARGS__), \
|
|
WasmFunctionResult##F, \
|
|
1); \
|
|
WasmEdge_String hook_api::WasmFunctionName##F = \
|
|
WasmEdge_StringCreateByCString(#F); \
|
|
R hook_api::F( \
|
|
hook::HookContext& hookCtx, \
|
|
WasmEdge_CallingFrameContext const& frameCtx, \
|
|
__VA_ARGS__)
|
|
|
|
#define DEFINE_HOOK_FUNCNARG(R, F) \
|
|
WasmEdge_Result hook_api::WasmFunction##F( \
|
|
void* data_ptr, \
|
|
const WasmEdge_CallingFrameContext* frameCtx, \
|
|
const WasmEdge_Value* in, \
|
|
WasmEdge_Value* out) \
|
|
{ \
|
|
hook::HookContext* hookCtx = \
|
|
reinterpret_cast<hook::HookContext*>(data_ptr); \
|
|
R return_code = hook_api::F( \
|
|
*hookCtx, *const_cast<WasmEdge_CallingFrameContext*>(frameCtx)); \
|
|
if (return_code == RC_ROLLBACK || return_code == RC_ACCEPT) \
|
|
return WasmEdge_Result_Terminate; \
|
|
out[0] = CAT2(RET_, R(return_code)); \
|
|
return WasmEdge_Result_Success; \
|
|
}; \
|
|
WasmEdge_ValType hook_api::WasmFunctionResult##F[1] = { \
|
|
WASM_VAL_TYPE(R, dummy)}; \
|
|
WasmEdge_FunctionTypeContext* hook_api::WasmFunctionType##F = \
|
|
WasmEdge_FunctionTypeCreate({}, 0, WasmFunctionResult##F, 1); \
|
|
WasmEdge_String hook_api::WasmFunctionName##F = \
|
|
WasmEdge_StringCreateByCString(#F); \
|
|
R hook_api::F( \
|
|
hook::HookContext& hookCtx, \
|
|
WasmEdge_CallingFrameContext const& frameCtx)
|
|
|
|
#define HOOK_SETUP() \
|
|
try \
|
|
{ \
|
|
[[maybe_unused]] ApplyContext& applyCtx = hookCtx.applyCtx; \
|
|
[[maybe_unused]] auto& view = applyCtx.view(); \
|
|
[[maybe_unused]] auto j = applyCtx.app.journal("View"); \
|
|
[[maybe_unused]] WasmEdge_MemoryInstanceContext* memoryCtx = \
|
|
WasmEdge_CallingFrameGetMemoryInstance(&frameCtx, 0); \
|
|
[[maybe_unused]] unsigned char* memory = \
|
|
WasmEdge_MemoryInstanceGetPointer(memoryCtx, 0, 0); \
|
|
[[maybe_unused]] const uint64_t memory_length = \
|
|
WasmEdge_MemoryInstanceGetPageSize(memoryCtx) * \
|
|
WasmEdge_kPageSize; \
|
|
if (!memoryCtx || !memory || !memory_length) \
|
|
return INTERNAL_ERROR;
|
|
|
|
#define HOOK_TEARDOWN() \
|
|
} \
|
|
catch (const std::exception& e) \
|
|
{ \
|
|
JLOG(hookCtx.applyCtx.app.journal("View").error()) \
|
|
<< "HookError[" << HC_ACC() << "]: " << __func__ \
|
|
<< " threw uncaught exception, what=" << e.what(); \
|
|
return INTERNAL_ERROR; \
|
|
}
|
|
|
|
#define WRITE_WASM_MEMORY( \
|
|
bytes_written, \
|
|
guest_dst_ptr, \
|
|
guest_dst_len, \
|
|
host_src_ptr, \
|
|
host_src_len, \
|
|
host_memory_ptr, \
|
|
guest_memory_length) \
|
|
{ \
|
|
int64_t bytes_to_write = std::min( \
|
|
static_cast<int64_t>(host_src_len), \
|
|
static_cast<int64_t>(guest_dst_len)); \
|
|
if (guest_dst_ptr + bytes_to_write > guest_memory_length) \
|
|
{ \
|
|
JLOG(j.warn()) << "HookError[" << HC_ACC() << "]: " << __func__ \
|
|
<< " tried to retreive blob of " << host_src_len \
|
|
<< " bytes past end of wasm memory"; \
|
|
return OUT_OF_BOUNDS; \
|
|
} \
|
|
if (!WasmEdge_ResultOK(WasmEdge_MemoryInstanceSetData( \
|
|
memoryCtx, \
|
|
reinterpret_cast<const uint8_t*>(host_src_ptr), \
|
|
guest_dst_ptr, \
|
|
bytes_to_write))) \
|
|
return INTERNAL_ERROR; \
|
|
bytes_written += bytes_to_write; \
|
|
}
|
|
|
|
#define WRITE_WASM_MEMORY_AND_RETURN( \
|
|
guest_dst_ptr, \
|
|
guest_dst_len, \
|
|
host_src_ptr, \
|
|
host_src_len, \
|
|
host_memory_ptr, \
|
|
guest_memory_length) \
|
|
{ \
|
|
int64_t bytes_written = 0; \
|
|
WRITE_WASM_MEMORY( \
|
|
bytes_written, \
|
|
guest_dst_ptr, \
|
|
guest_dst_len, \
|
|
host_src_ptr, \
|
|
host_src_len, \
|
|
host_memory_ptr, \
|
|
guest_memory_length); \
|
|
return bytes_written; \
|
|
}
|
|
|
|
// ptr = pointer inside the wasm memory space
|
|
#define NOT_IN_BOUNDS(ptr, len, memory_length) \
|
|
((static_cast<uint64_t>(ptr) >= static_cast<uint64_t>(memory_length)) || \
|
|
((static_cast<uint64_t>(ptr) + static_cast<uint64_t>(len)) > \
|
|
static_cast<uint64_t>(memory_length)))
|
|
|
|
#define HOOK_EXIT(read_ptr, read_len, error_code, exit_type) \
|
|
{ \
|
|
if (read_len > 256) \
|
|
read_len = 256; \
|
|
if (read_ptr) \
|
|
{ \
|
|
if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length)) \
|
|
{ \
|
|
JLOG(j.warn()) << "HookError[" << HC_ACC() << "]: " \
|
|
<< "Tried to accept/rollback but specified " \
|
|
"memory outside of the wasm instance " \
|
|
<< "limit when specifying a reason string"; \
|
|
return OUT_OF_BOUNDS; \
|
|
} \
|
|
/* assembly script and some other languages use utf16 for strings \
|
|
*/ \
|
|
if (is_UTF16LE(read_ptr + memory, read_len)) \
|
|
{ \
|
|
uint8_t output[128]; \
|
|
int len = read_len / 2; /* is_UTF16LE will only return true if \
|
|
read_len is even */ \
|
|
for (int i = 0; i < len; ++i) \
|
|
output[i] = memory[read_ptr + i * 2]; \
|
|
hookCtx.result.exitReason = \
|
|
std::string((const char*)(output), (size_t)len); \
|
|
} \
|
|
else \
|
|
hookCtx.result.exitReason = std::string( \
|
|
(const char*)(memory + read_ptr), (size_t)read_len); \
|
|
} \
|
|
hookCtx.result.exitType = exit_type; \
|
|
hookCtx.result.exitCode = error_code; \
|
|
return ( \
|
|
exit_type == hook_api::ExitType::ACCEPT ? RC_ACCEPT \
|
|
: RC_ROLLBACK); \
|
|
}
|
|
|
|
#define WRITE_WASM_MEMORY_OR_RETURN_AS_INT64( \
|
|
write_ptr_in, write_len_in, data_ptr_in, data_len_in, is_account_in) \
|
|
{ \
|
|
uint8_t* data_ptr = (uint8_t*)(data_ptr_in); \
|
|
int data_len = (data_len_in); \
|
|
if (is_account_in) \
|
|
{ \
|
|
data_len--; \
|
|
data_ptr++; \
|
|
} \
|
|
if (data_len < 0 || data_len > (data_len_in) || \
|
|
data_ptr < (data_ptr_in)) \
|
|
return INTERNAL_ERROR; \
|
|
if (data_len == 0) \
|
|
return 0; \
|
|
if ((write_ptr_in) == 0) \
|
|
return data_as_int64(data_ptr, data_len); \
|
|
if (data_len > (write_len_in)) \
|
|
return TOO_SMALL; \
|
|
WRITE_WASM_MEMORY_AND_RETURN( \
|
|
(write_ptr_in), \
|
|
(write_len_in), \
|
|
data_ptr, \
|
|
data_len, \
|
|
memory, \
|
|
memory_length); \
|
|
}
|