/** * 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(data_ptr); \ R return_code = hook_api::F( \ *hookCtx, \ *const_cast(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(data_ptr); \ R return_code = hook_api::F( \ *hookCtx, *const_cast(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(host_src_len), \ static_cast(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(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(ptr) >= static_cast(memory_length)) || \ ((static_cast(ptr) + static_cast(len)) > \ static_cast(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); \ }