diff --git a/ebpfapi/rpc_client.cpp b/ebpfapi/rpc_client.cpp index 0f000b796..52bc7d62c 100644 --- a/ebpfapi/rpc_client.cpp +++ b/ebpfapi/rpc_client.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,11 @@ static const wchar_t* _protocol_sequence = L"ncalrpc"; static bool _binding_initialized = false; +static std::mutex _rpc_binding_handle_mutex; + +static RPC_STATUS +_initialize_rpc_binding(); + _Must_inspect_result_ ebpf_result_t ebpf_rpc_load_program( _In_ const ebpf_program_load_info* info, @@ -31,6 +37,10 @@ ebpf_rpc_load_program( { ebpf_result_t result; + if (_initialize_rpc_binding() != RPC_S_OK) { + return EBPF_NO_MEMORY; + } + RpcTryExcept { result = ebpf_client_verify_and_load_program( @@ -50,9 +60,22 @@ ebpf_rpc_load_program( return result; } -RPC_STATUS -initialize_rpc_binding() +/** + * @brief Initialize the RPC binding handle. This is expensive and should be + * done once when required. This function is idempotent and thread safe. + * + * @retval RPC_S_OK The binding handle was initialized successfully. + * @retval RPC_S_* The binding handle could not be initialized. + */ +static RPC_STATUS +_initialize_rpc_binding() { + std::unique_lock lock(_rpc_binding_handle_mutex); + + if (_binding_initialized) { + return RPC_S_OK; + } + RPC_WSTR string_binding = nullptr; RPC_SECURITY_QOS_V5 rpc_security_qos{ RPC_C_SECURITY_QOS_VERSION_5, @@ -121,10 +144,13 @@ Exit: RPC_STATUS clean_up_rpc_binding() { + std::unique_lock lock(_rpc_binding_handle_mutex); if (!_binding_initialized) { return RPC_S_OK; } - return RpcBindingFree(&ebpf_service_interface_handle); + RPC_STATUS status = RpcBindingFree(&ebpf_service_interface_handle); + _binding_initialized = false; + return status; } // The _In_ on size is necessary to avoid inconsistent annotation warnings. diff --git a/libs/api/ebpf_api.cpp b/libs/api/ebpf_api.cpp index c2d57bbb5..755474d00 100644 --- a/libs/api/ebpf_api.cpp +++ b/libs/api/ebpf_api.cpp @@ -204,16 +204,6 @@ ebpf_api_initiate() noexcept // it will be re-attempted before an IOCTL call is made. (void)initialize_device_handle(); -#if !defined(CONFIG_BPF_JIT_DISABLED) || !defined(CONFIG_BPF_INTERPRETER_DISABLED) - RPC_STATUS status = initialize_rpc_binding(); - - if (status != RPC_S_OK) { - clean_up_device_handle(); - clean_up_rpc_binding(); - EBPF_RETURN_RESULT(win32_error_code_to_ebpf_result(status)); - } -#endif - // Load provider data from ebpf store. This is best effort // as there may be no data present in the store. (void)load_ebpf_provider_data(); diff --git a/libs/api/rpc_client.h b/libs/api/rpc_client.h index 48e7c98f1..ea964b6b3 100644 --- a/libs/api/rpc_client.h +++ b/libs/api/rpc_client.h @@ -7,9 +7,6 @@ #include -RPC_STATUS -initialize_rpc_binding(void); - RPC_STATUS clean_up_rpc_binding(void); diff --git a/libs/thunk/mock/mock.cpp b/libs/thunk/mock/mock.cpp index e5756324f..8d3a59153 100644 --- a/libs/thunk/mock/mock.cpp +++ b/libs/thunk/mock/mock.cpp @@ -189,9 +189,6 @@ _stop_service(SC_HANDLE service_handle) #if !defined(CONFIG_BPF_JIT_DISABLED) || !defined(CONFIG_BPF_INTERPRETER_DISABLED) // RPC related mock functions. -RPC_STATUS -initialize_rpc_binding() { return RPC_S_OK; } - RPC_STATUS clean_up_rpc_binding() { return RPC_S_OK; }