From cf2ef87325a1bf62cb5fe898ea077fe46ee6ba5a Mon Sep 17 00:00:00 2001 From: saxena-anurag <43585259+saxena-anurag@users.noreply.github.com> Date: Mon, 7 Jun 2021 14:22:04 -0700 Subject: [PATCH] Move program load to ebpfsvc (#245) * remove duplicate windows_helpers.cpp, refactor * remove commented code * move program load to service * cleanup commented code * remove commented code * cr comments * make device handle init optional * cr comments * change return type of some functions * fix * cr comments * cr comments, cover more error conditions in windows_error_to_ebpf_result * cr comments Co-authored-by: Dave Thaler Co-authored-by: Alan Jowett --- docs/GettingStarted.md | 20 +- ebpf-for-windows.sln | 1 + ebpfapi/ebpfapi.vcxproj | 8 +- ebpfapi/ebpfapi.vcxproj.filters | 3 + ebpfapi/rpc_client.cpp | 85 ++++ ebpfsvc/eBPFSvc.vcxproj | 2 +- ebpfsvc/rpc_api.cpp | 42 +- ebpfsvc/rpc_util.cpp | 52 ++- ebpfsvc/svcmain.cpp | 8 + include/ebpf_api.h | 13 +- include/ebpf_execution_type.h | 19 + include/ebpf_result.h | 14 +- libs/api/Verifier.cpp | 6 + libs/api/api.vcxproj | 5 +- libs/api/api.vcxproj.filters | 3 + libs/api/ebpf_api.cpp | 435 +++++------------- {tests/client => libs/api}/rpc_client.h | 7 +- libs/api/windows_platform.cpp | 2 +- libs/api_common/api_common.cpp | 34 +- libs/api_common/api_common.hpp | 102 +++- libs/api_common/api_common.vcxproj | 3 +- libs/api_common/api_common.vcxproj.filters | 5 +- libs/api_common/device_helper.cpp | 68 +++ ...{windows_helpers.hpp => device_helper.hpp} | 18 +- libs/api_common/map_descriptors.cpp | 29 +- libs/api_common/map_descriptors.hpp | 9 +- libs/api_common/windows_helpers.cpp | 23 +- libs/service/api_service.cpp | 360 ++++++++++++++- libs/service/api_service.h | 28 +- libs/service/service.vcxproj | 4 +- libs/service/verifier_service.cpp | 14 +- libs/service/verifier_service.h | 2 +- rpc_interface/rpc_interface.idl | 8 +- rpc_interface/rpc_interface.vcxproj | 1 + tests/client/ebpf_client.vcxproj | 4 +- tests/client/ebpf_client.vcxproj.filters | 8 +- tests/client/rpc_client.cpp | 80 ---- tests/end_to_end/end_to_end.vcxproj | 4 +- tests/end_to_end/mock.cpp | 33 +- 39 files changed, 1028 insertions(+), 534 deletions(-) create mode 100644 ebpfapi/rpc_client.cpp create mode 100644 include/ebpf_execution_type.h rename {tests/client => libs/api}/rpc_client.h (61%) create mode 100644 libs/api_common/device_helper.cpp rename libs/api_common/{windows_helpers.hpp => device_helper.hpp} (89%) delete mode 100644 tests/client/rpc_client.cpp diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index ba76cf887..5b933504f 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -32,6 +32,7 @@ This will build the following binaries: loading eBPF programs. * ebpfnetsh.dll: A plugin for the Windows netsh.exe command line tool that provides eBPF command line utility functionality. +* ebpfsvc.exe: A user-mode service that verifies and loads an eBPF program in the execution context. * end_to_end.exe: A collection of tests using the Catch framework. These tests are also run as part of the Github CI/CD so should always pass. @@ -60,14 +61,17 @@ On the defender machine, do the following: 3. Install Debug VS 2019 VC redist from TBD (or switch everything to Multi-threaded Debug (/MTd) and rebuild) 4. Copy ebpfcore.sys to %windir%\system32\drivers 5. Copy netebpfext.sys to %windir%\system32\drivers -6. Copy ebpfapi.dll and ebpfnetsh.dll to %windir%\system32 -7. Do `sc create EbpfCore type=kernel start=boot binpath=%windir%\system32\drivers\ebpfcore.sys` -8. Do `sc start EbpfCore` -9. Do `sc create NetEbpfExt type=kernel start=boot binpath=%windir%\system32\drivers\netebpfext.sys` -10. Do `sc start NetEbpfExt` -11. Do `netsh add helper %windir%\system32\ebpfnetsh.dll` -12. Install [clang](https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/LLVM-11.0.0-win64.exe) -13. Copy droppacket.c and ebpf.h to a folder (such as c:\test) +6. Copy ebpfsvc.exe to %windir%\system32 +7. Copy ebpfapi.dll and ebpfnetsh.dll to %windir%\system32 +8. Do `sc create EbpfCore type=kernel start=boot binpath=%windir%\system32\drivers\ebpfcore.sys` +9. Do `sc start EbpfCore` +10. Do `sc create NetEbpfExt type=kernel start=boot binpath=%windir%\system32\drivers\netebpfext.sys` +11. Do `sc start NetEbpfExt` +12. Do `sc create ebpfsvc start= auto binpath=%windir%\system32\ebpfsvc.exe type=own` +13. Do `sc start ebpfsvc` +14. Do `netsh add helper %windir%\system32\ebpfnetsh.dll` +15. Install [clang](https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/LLVM-11.0.0-win64.exe) +16. Copy droppacket.c and ebpf.h to a folder (such as c:\test) On the attacker machine, do the following: 1. Copy DnsFlood.exe to attacker machine diff --git a/ebpf-for-windows.sln b/ebpf-for-windows.sln index 526c96b0f..df31d77a7 100644 --- a/ebpf-for-windows.sln +++ b/ebpf-for-windows.sln @@ -46,6 +46,7 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EbpfApi", "ebpfapi\ebpfapi.vcxproj", "{75FE223A-3E45-4B0E-A2E8-04285E52E440}" ProjectSection(ProjectDependencies) = postProject {231EE32B-EBA4-4FE5-A55B-DB18F539D403} = {231EE32B-EBA4-4FE5-A55B-DB18F539D403} + {1423245D-0249-40FC-A077-FF7780ACFE3F} = {1423245D-0249-40FC-A077-FF7780ACFE3F} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ebpfnetsh", "tools\netsh\ebpfnetsh.vcxproj", "{74803F80-A8BD-4A03-862B-FA96648A9BF6}" diff --git a/ebpfapi/ebpfapi.vcxproj b/ebpfapi/ebpfapi.vcxproj index a1e094a35..923d0378d 100644 --- a/ebpfapi/ebpfapi.vcxproj +++ b/ebpfapi/ebpfapi.vcxproj @@ -74,7 +74,7 @@ true pch.h MultiThreadedDebugDLL - $(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context + $(SolutionDir)libs\api;$(SolutionDir)rpc_interface;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context true stdcpp17 @@ -96,7 +96,7 @@ NDEBUG;EBPFAPI_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true pch.h - $(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context + $(SolutionDir)libs\api;$(SolutionDir)rpc_interface;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context MultiThreadedDLL true stdcpp17 @@ -120,6 +120,7 @@ + @@ -137,9 +138,6 @@ {c26cb6a9-158c-4a9e-a243-755ddd98e5fe} - - {af85c549-57cc-40a5-bdfc-dcf1998de80f} - {245f0ec7-1ebc-4d68-8b1f-f758ea9196ae} diff --git a/ebpfapi/ebpfapi.vcxproj.filters b/ebpfapi/ebpfapi.vcxproj.filters index 82b93b05d..84309a6b1 100644 --- a/ebpfapi/ebpfapi.vcxproj.filters +++ b/ebpfapi/ebpfapi.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + diff --git a/ebpfapi/rpc_client.cpp b/ebpfapi/rpc_client.cpp new file mode 100644 index 000000000..7e211092b --- /dev/null +++ b/ebpfapi/rpc_client.cpp @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include "ebpf_api.h" +#include "ebpf_windows.h" +#include "rpc_interface_c.c" + +#pragma comment(lib, "Rpcrt4.lib") + +static RPC_WSTR _string_binding = nullptr; +static const WCHAR* _protocol_sequence = L"ncalrpc"; +static bool _binding_initialized = false; + +ebpf_result_t +ebpf_rpc_verify_program(ebpf_program_verify_info* info, const char** logs, uint32_t* logs_size) noexcept +{ + ebpf_result_t result; + + RpcTryExcept { result = ebpf_client_verify_program(info, logs_size, const_cast(logs)); } + RpcExcept(RpcExceptionFilter(RpcExceptionCode())) + { + // TODO: (Issue# 247) Add tracing for the RpcExceptionCode() that is returned. + result = EBPF_RPC_EXCEPTION; + } + RpcEndExcept + + return result; +} + +ebpf_result_t +ebpf_rpc_load_program(ebpf_program_load_info* info, const char** logs, uint32_t* logs_size) noexcept +{ + ebpf_result_t result; + + RpcTryExcept { result = ebpf_client_verify_and_load_program(info, logs_size, const_cast(logs)); } + RpcExcept(RpcExceptionFilter(RpcExceptionCode())) + { + // TODO: (Issue# 247) Add tracing for the RpcExceptionCode() that is returned. + result = EBPF_RPC_EXCEPTION; + } + RpcEndExcept + + return result; +} + +RPC_STATUS +initialize_rpc_binding() +{ + RPC_STATUS status = + RpcStringBindingCompose(nullptr, (RPC_WSTR)_protocol_sequence, nullptr, nullptr, nullptr, &_string_binding); + + if (status != RPC_S_OK) { + return status; + } + + status = RpcBindingFromStringBinding(_string_binding, &ebpf_service_interface_handle); + if (status == RPC_S_OK) { + _binding_initialized = true; + } + + return status; +} + +RPC_STATUS +clean_up_rpc_binding() +{ + RPC_STATUS status = RpcStringFree(&_string_binding); + if (status != RPC_S_OK) { + printf("RpcStringFree failed with error %d\n", status); + } + + if (_binding_initialized) { + status = RpcBindingFree(&ebpf_service_interface_handle); + if (status != RPC_S_OK) { + printf("RpcBindingFree failed with error %d\n", status); + } + } + + return status; +} diff --git a/ebpfsvc/eBPFSvc.vcxproj b/ebpfsvc/eBPFSvc.vcxproj index 83506e197..aa91a769d 100644 --- a/ebpfsvc/eBPFSvc.vcxproj +++ b/ebpfsvc/eBPFSvc.vcxproj @@ -134,7 +134,7 @@ NotUsing pch.h MultiThreadedDebugDLL - $(SolutionDir)libs\api;$(SolutionDir)libs\platform;$(SolutionDir)libs\service;$(SolutionDir)include;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform\user;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;%(AdditionalIncludeDirectories) + $(SolutionDir)libs\api_common;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(SolutionDir)libs\platform;$(SolutionDir)libs\service;$(SolutionDir)include;$(SolutionDir)rpc_interface;$(SolutionDir)libs\platform\user;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;%(AdditionalIncludeDirectories) stdcpplatest diff --git a/ebpfsvc/rpc_api.cpp b/ebpfsvc/rpc_api.cpp index 674c74e1f..4d7377ef4 100644 --- a/ebpfsvc/rpc_api.cpp +++ b/ebpfsvc/rpc_api.cpp @@ -5,29 +5,36 @@ #include #include #include -#include "api_internal.h" #include "api_service.h" -#include "ebpf_windows.h" #include "rpc_interface_h.h" #include "svc_common.h" -#include "Verifier.h" - -// Critical section to serialize RPC calls. -// Currently ebpfsvc uses a global context to track verification -// and JIT compilation, hence all RPC calls should be serialized. -static std::mutex _mutex; ebpf_result_t -ebpf_server_verify_and_jit_program( +ebpf_server_verify_and_load_program( /* [ref][in] */ ebpf_program_load_info* info, /* [ref][out] */ uint32_t* logs_size, /* [ref][size_is][size_is][out] */ char** logs) { - UNREFERENCED_PARAMETER(info); - UNREFERENCED_PARAMETER(logs_size); - UNREFERENCED_PARAMETER(logs); + ebpf_result_t result; - return EBPF_FAILED; + if (info->byte_code_size == 0) { + return EBPF_INVALID_ARGUMENT; + } + + result = ebpf_verify_and_load_program( + &info->program_type, + info->program_handle, + info->execution_context, + info->execution_type, + info->map_count, + info->handle_map, + info->byte_code_size, + info->byte_code, + const_cast(logs), + logs_size); + + ebpf_clear_thread_local_storage(); + return result; } ebpf_result_t @@ -36,17 +43,17 @@ ebpf_server_verify_program( /* [out] */ uint32_t* logs_size, /* [ref][size_is][size_is][out] */ char** logs) { + ebpf_result_t result; + if (info->byte_code_size == 0) { return EBPF_INVALID_ARGUMENT; } - std::scoped_lock lock(_mutex); - // MIDL generates warnings if any [out] param uses 'const', // since RPC marshaling will copy the data anyway. So we // can safely cast the 'logs' param below. - return ebpf_verify_program( + result = ebpf_verify_program( reinterpret_cast(&info->program_type), info->execution_context, info->map_descriptors_count, @@ -55,4 +62,7 @@ ebpf_server_verify_program( info->byte_code, const_cast(logs), logs_size); + + ebpf_clear_thread_local_storage(); + return result; } diff --git a/ebpfsvc/rpc_util.cpp b/ebpfsvc/rpc_util.cpp index 5f853e957..d91c8bfda 100644 --- a/ebpfsvc/rpc_util.cpp +++ b/ebpfsvc/rpc_util.cpp @@ -7,38 +7,46 @@ #pragma comment(lib, "Rpcrt4.lib") -#define RPC_SERVER_ENDPOINT L"\\pipe\\ebpf_service" +#define ANNOTATION L"ebpfsvc rpc server" +#define EBPF_SERVICE_INTERFACE_HANDLE ebpf_server_ebpf_service_interface_v1_0_s_ifspec + +static const WCHAR* _protocol_sequence = L"ncalrpc"; static bool _rpc_server_initialized = false; DWORD initialize_rpc_server() { RPC_STATUS status; - const WCHAR* protocol_sequence = L"ncacn_np"; - unsigned char* security = nullptr; - const WCHAR* endpoint = RPC_SERVER_ENDPOINT; - unsigned int minimum_calls = 1; - unsigned int dont_wait = true; bool registered = false; + RPC_BINDING_VECTOR* binding_vector = nullptr; - status = RpcServerUseProtseqEp( - (RPC_WSTR)protocol_sequence, RPC_C_LISTEN_MAX_CALLS_DEFAULT, (RPC_WSTR)endpoint, security); + status = RpcServerUseProtseq((RPC_WSTR)_protocol_sequence, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, nullptr); if (status != RPC_S_OK) { goto Exit; } - status = RpcServerRegisterIf(ebpf_server_ebpf_service_interface_v1_0_s_ifspec, nullptr, nullptr); + status = RpcServerRegisterIfEx( + EBPF_SERVICE_INTERFACE_HANDLE, nullptr, nullptr, RPC_IF_AUTOLISTEN, RPC_C_LISTEN_MAX_CALLS_DEFAULT, nullptr); if (status != RPC_S_OK) { goto Exit; } registered = true; - status = RpcServerListen(minimum_calls, RPC_C_LISTEN_MAX_CALLS_DEFAULT, dont_wait); + status = RpcServerInqBindings(&binding_vector); + if (status != RPC_S_OK) { + goto Exit; + } + + status = RpcEpRegister(EBPF_SERVICE_INTERFACE_HANDLE, binding_vector, NULL, (RPC_WSTR)ANNOTATION); if (status == RPC_S_OK) { _rpc_server_initialized = true; } + Exit: + if (binding_vector != nullptr) { + RpcBindingVectorFree(&binding_vector); + } if (status != RPC_S_OK) { if (registered) { RpcServerUnregisterIf(nullptr, nullptr, true); @@ -50,22 +58,32 @@ Exit: void shutdown_rpc_server() { + RPC_STATUS status; + RPC_BINDING_VECTOR* binding_vector = nullptr; + if (!_rpc_server_initialized) { return; } - RPC_STATUS status; - status = RpcMgmtStopServerListening(nullptr); + status = RpcServerInqBindings(&binding_vector); if (status != RPC_S_OK) { - // TODO: Add a trace that something happened. - return; + goto Exit; } - status = RpcServerUnregisterIf(nullptr, nullptr, true); + status = RpcEpUnregister(EBPF_SERVICE_INTERFACE_HANDLE, binding_vector, nullptr); if (status != RPC_S_OK) { - // TODO: Add a trace that something happened. - return; + goto Exit; } + status = RpcServerUnregisterIf(EBPF_SERVICE_INTERFACE_HANDLE, nullptr, true); + if (status != RPC_S_OK) { + // TODO: Add a trace that something happened. + goto Exit; + } + +Exit: + if (binding_vector != nullptr) { + RpcBindingVectorFree(&binding_vector); + } return; } diff --git a/ebpfsvc/svcmain.cpp b/ebpfsvc/svcmain.cpp index d3905a247..922d8d3d0 100644 --- a/ebpfsvc/svcmain.cpp +++ b/ebpfsvc/svcmain.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT +#include "api_service.h" #include "rpc_util.h" #include "svc_common.h" @@ -198,7 +199,13 @@ Initialize() { DWORD status; status = initialize_rpc_server(); + if (status != ERROR_SUCCESS) { + goto Exit; + } + status = ebpf_service_initialize(); + +Exit: return status; } @@ -206,6 +213,7 @@ void Cleanup() { shutdown_rpc_server(); + ebpf_service_cleanup(); if (ebpf_service_stop_event_handle) { CloseHandle(ebpf_service_stop_event_handle); } diff --git a/include/ebpf_api.h b/include/ebpf_api.h index 5a42d49b4..e805aec0b 100644 --- a/include/ebpf_api.h +++ b/include/ebpf_api.h @@ -7,7 +7,8 @@ #include #include - +#include "ebpf_execution_type.h" +#include "ebpf_result.h" #include "ebpf_windows.h" #ifdef __cplusplus @@ -33,12 +34,6 @@ extern "C" const ebpf_handle_t ebpf_handle_invalid = (ebpf_handle_t)-1; typedef struct _tlv_type_length_value tlv_type_length_value_t; - typedef enum _ebpf_execution_type - { - EBPF_EXECUTION_JIT, - EBPF_EXECUTION_INTERPRET - } ebpf_execution_type_t; - /** * @brief Initialize the eBPF user mode library. */ @@ -63,7 +58,7 @@ extern "C" * @param[out] map_handles Array of map handles to be filled in. * @param[out] error_message Error message describing what failed. */ - uint32_t + ebpf_result_t ebpf_api_load_program( const char* file, const char* section_name, @@ -148,7 +143,7 @@ extern "C" * @param[out] value_size Size of values in the eBPF map. * @param[out] max_entries Maximum number of entries in the map. */ - uint32_t + ebpf_result_t ebpf_api_map_query_definition( ebpf_handle_t handle, uint32_t* size, diff --git a/include/ebpf_execution_type.h b/include/ebpf_execution_type.h new file mode 100644 index 000000000..e80d1dfd8 --- /dev/null +++ b/include/ebpf_execution_type.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef enum _ebpf_execution_type + { + EBPF_EXECUTION_JIT, + EBPF_EXECUTION_INTERPRET + } ebpf_execution_type_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/ebpf_result.h b/include/ebpf_result.h index a27997cf5..8f85cba75 100644 --- a/include/ebpf_result.h +++ b/include/ebpf_result.h @@ -18,8 +18,11 @@ extern "C" // Program verification failed. EBPF_VALIDATION_FAILED, + // JIT compilation failed. + EBPF_JIT_COMPILATION_FAILED, + // Program load failed. - EBPF_LOAD_FAILED, + EBPF_PROGRAM_LOAD_FAILED, // Invalid FD provided. EBPF_INVALID_FD, @@ -48,6 +51,15 @@ extern "C" // Low memory. EBPF_NO_MEMORY, + // The program is too large. + EBPF_PROGRAM_TOO_LARGE, + + // RPC exception. + EBPF_RPC_EXCEPTION, + + // Handle is already initialized. + EBPF_ALREADY_INITIALIZED, + // Generic failure code for all other errors. EBPF_FAILED, diff --git a/libs/api/Verifier.cpp b/libs/api/Verifier.cpp index 59be04da5..b882176a4 100644 --- a/libs/api/Verifier.cpp +++ b/libs/api/Verifier.cpp @@ -51,6 +51,12 @@ load_byte_code( // copy out the bytecode for the jitter size_t ebpf_bytes = raw_prog.prog.size() * sizeof(ebpf_inst); + + // Check if the raw program can fit in the supplied buffer. + if (ebpf_bytes > *byte_code_size) { + return ERROR_BUFFER_OVERFLOW; + } + int i = 0; for (ebpf_inst inst : raw_prog.prog) { char* buf = (char*)&inst; diff --git a/libs/api/api.vcxproj b/libs/api/api.vcxproj index e1e23777b..df95725a5 100644 --- a/libs/api/api.vcxproj +++ b/libs/api/api.vcxproj @@ -131,7 +131,7 @@ true NotUsing pch.h - $(SolutionDir)libs\service;$(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories) + $(SolutionDir)libs\api;$(SolutionDir)rpc_interface;$(SolutionDir)libs\service;$(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories) stdcpplatest MultiThreadedDebugDLL true @@ -152,7 +152,7 @@ true NotUsing pch.h - $(SolutionDir)libs\service;$(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories) + $(SolutionDir)rpc_interface;$(SolutionDir)libs\service;$(SolutionDir)libs\api_common;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories) stdcpplatest true @@ -176,6 +176,7 @@ + diff --git a/libs/api/api.vcxproj.filters b/libs/api/api.vcxproj.filters index 8badd30f2..544a54720 100644 --- a/libs/api/api.vcxproj.filters +++ b/libs/api/api.vcxproj.filters @@ -45,5 +45,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/libs/api/ebpf_api.cpp b/libs/api/ebpf_api.cpp index c493164d7..1ecc8b010 100644 --- a/libs/api/ebpf_api.cpp +++ b/libs/api/ebpf_api.cpp @@ -2,55 +2,44 @@ // SPDX-License-Identifier: MIT #include "pch.h" -#include "ebpf_api.h" -#include -#include #include "api_common.hpp" +#include "device_helper.hpp" +#include "ebpf_api.h" #include "ebpf_protocol.h" -#include "ebpf_platform.h" #include "map_descriptors.hpp" -#include "platform.h" +#include "rpc_client.h" extern "C" { #include "ubpf.h" } #include "Verifier.h" -#include "verifier_service.h" -#include "windows_helpers.hpp" #define MAX_CODE_SIZE (32 * 1024) // 32 KB uint32_t ebpf_api_initiate() { - if (device_handle != INVALID_HANDLE_VALUE) { - return ERROR_ALREADY_INITIALIZED; + uint32_t result; + + // This is best effort. If device handle does not initialize, + // it will be re-attempted before an IOCTL call is made. + initialize_device_handle(); + + result = initialize_rpc_binding(); + + if (result != ERROR_SUCCESS) { + clean_up_device_handle(); + clean_up_rpc_binding(); } - - device_handle = Platform::CreateFile( - EBPF_DEVICE_WIN32_NAME, - GENERIC_READ | GENERIC_WRITE, - 0, - nullptr, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - nullptr); - - if (device_handle == INVALID_HANDLE_VALUE) { - return GetLastError(); - } - - return 0; + return result; } void ebpf_api_terminate() { - if (device_handle != INVALID_HANDLE_VALUE) { - Platform::CloseHandle(device_handle); - device_handle = INVALID_HANDLE_VALUE; - } + clean_up_device_handle(); + clean_up_rpc_binding(); } int @@ -64,7 +53,7 @@ create_map_function( _ebpf_operation_create_map_reply reply{}; - uint32_t retval = invoke_ioctl(device_handle, request, reply); + uint32_t retval = invoke_ioctl(request, reply); if (retval != ERROR_SUCCESS) { throw std::runtime_error(std::string("Error ") + std::to_string(retval) + " trying to create map"); } @@ -73,165 +62,10 @@ create_map_function( throw std::runtime_error(std::string("reply.header.id != ebpf_operation_id_t::EBPF_OPERATION_CREATE_MAP")); } - return cache_map_handle(reply.handle, type, key_size, value_size); + return cache_map_handle(reply.handle, type, key_size, value_size, max_entries); } -static uint32_t -resolve_maps_in_byte_code(ebpf_handle_t program_handle, ebpf_code_buffer_t& byte_code) -{ - // Maintain two maps. - // First map is instruction offset -> map handle. - // Second map is map handle -> map address. - std::map instruction_offsets_to_map_handles; - std::map map_handles_to_map_addresses; - - ebpf_inst* instructions = reinterpret_cast(byte_code.data()); - ebpf_inst* instruction_end = reinterpret_cast(byte_code.data() + byte_code.size()); - for (size_t index = 0; index < byte_code.size() / sizeof(ebpf_inst); index++) { - ebpf_inst& first_instruction = instructions[index]; - ebpf_inst& second_instruction = instructions[index + 1]; - if (first_instruction.opcode != INST_OP_LDDW_IMM) { - continue; - } - if (&instructions[index + 1] >= instruction_end) { - return ERROR_INVALID_PARAMETER; - } - index++; - - // Check for LD_MAP flag - if (first_instruction.src != 1) { - continue; - } - - uint64_t imm = - static_cast(first_instruction.imm) | (static_cast(second_instruction.imm) << 32); - - // Collect set of instructions to patch with the value to replace. - instruction_offsets_to_map_handles[index - 1] = imm; - - // Collect set of map handles. - map_handles_to_map_addresses[imm] = 0; - } - - if (instruction_offsets_to_map_handles.size() == 0) { - return ERROR_SUCCESS; - } - - ebpf_protocol_buffer_t request_buffer( - offsetof(ebpf_operation_resolve_map_request_t, map_handle) + - sizeof(uint64_t) * map_handles_to_map_addresses.size()); - - ebpf_protocol_buffer_t reply_buffer( - offsetof(ebpf_operation_resolve_map_reply_t, address) + sizeof(uint64_t) * map_handles_to_map_addresses.size()); - - auto request = reinterpret_cast(request_buffer.data()); - auto reply = reinterpret_cast(reply_buffer.data()); - request->header.id = ebpf_operation_id_t::EBPF_OPERATION_RESOLVE_MAP; - request->header.length = static_cast(request_buffer.size()); - request->program_handle = reinterpret_cast(program_handle); - - size_t index = 0; - for (auto& [map_handle, map_address] : map_handles_to_map_addresses) { - - if (map_handle > get_map_descriptor_size()) { - return ERROR_INVALID_PARAMETER; - } - request->map_handle[index++] = get_map_handle_at_index(map_handle - 1); - } - - uint32_t result = invoke_ioctl(device_handle, request_buffer, reply_buffer); - if (result != ERROR_SUCCESS) { - return result; - } - - index = 0; - for (auto& [map_handle, map_address] : map_handles_to_map_addresses) { - map_address = reply->address[index++]; - } - - for (auto& [instruction_offset, map_handle] : instruction_offsets_to_map_handles) { - ebpf_inst& first_instruction = instructions[instruction_offset]; - ebpf_inst& second_instruction = instructions[instruction_offset + 1]; - - // Clear LD_MAP flag - first_instruction.src = 0; - - // Replace handle with address - uint64_t new_imm = map_handles_to_map_addresses[map_handle]; - first_instruction.imm = static_cast(new_imm); - second_instruction.imm = static_cast(new_imm >> 32); - } - - return ERROR_SUCCESS; -} - -static uint32_t -build_helper_id_to_address_map( - ebpf_handle_t program_handle, ebpf_code_buffer_t& byte_code, std::map& helper_id_to_adddress) -{ - ebpf_inst* instructions = reinterpret_cast(byte_code.data()); - for (size_t index = 0; index < byte_code.size() / sizeof(ebpf_inst); index++) { - ebpf_inst& instruction = instructions[index]; - if (instruction.opcode != INST_OP_CALL) { - continue; - } - helper_id_to_adddress[instruction.imm] = 0; - } - - if (helper_id_to_adddress.size() == 0) - return ERROR_SUCCESS; - - ebpf_protocol_buffer_t request_buffer( - offsetof(ebpf_operation_resolve_helper_request_t, helper_id) + sizeof(uint32_t) * helper_id_to_adddress.size()); - - ebpf_protocol_buffer_t reply_buffer( - offsetof(ebpf_operation_resolve_helper_reply_t, address) + sizeof(uint64_t) * helper_id_to_adddress.size()); - - auto request = reinterpret_cast(request_buffer.data()); - auto reply = reinterpret_cast(reply_buffer.data()); - request->header.id = ebpf_operation_id_t::EBPF_OPERATION_RESOLVE_HELPER; - request->header.length = static_cast(request_buffer.size()); - request->program_handle = reinterpret_cast(program_handle); - - size_t index = 0; - for (const auto& helper : helper_id_to_adddress) { - request->helper_id[index++] = helper.first; - } - - uint32_t result = invoke_ioctl(device_handle, request_buffer, reply_buffer); - if (result != ERROR_SUCCESS) { - return result; - } - - index = 0; - for (auto& helper : helper_id_to_adddress) { - helper.second = reply->address[index++]; - } - - return EBPF_SUCCESS; -} - -static uint32_t -resolve_ec_function(ebpf_ec_function_t function, uint64_t* address) -{ - ebpf_operation_get_ec_function_request_t request = {sizeof(request), EBPF_OPERATION_GET_EC_FUNCTION, function}; - ebpf_operation_get_ec_function_reply_t reply; - - uint32_t retval = invoke_ioctl(device_handle, request, reply); - if (retval != ERROR_SUCCESS) { - return retval; - } - - if (reply.header.id != ebpf_operation_id_t::EBPF_OPERATION_GET_EC_FUNCTION) { - return ERROR_INVALID_PARAMETER; - } - - *address = reply.address; - - return retval; -} - -static uint32_t +static ebpf_result_t _create_program( ebpf_program_type_t program_type, const std::string& file_name, @@ -258,12 +92,14 @@ _create_program( std::copy(section_name.begin(), section_name.end(), request_buffer.begin() + request->section_name_offset); - uint32_t retval = invoke_ioctl(device_handle, request_buffer, reply); - if (retval != ERROR_SUCCESS) { - return retval; + uint32_t error = invoke_ioctl(request_buffer, reply); + if (error != ERROR_SUCCESS) { + goto Exit; } *program_handle = reinterpret_cast(reply.program_handle); - return retval; + +Exit: + return windows_error_to_ebpf_result(error); } uint32_t @@ -328,7 +164,7 @@ Done: return result; } -uint32_t +ebpf_result_t ebpf_api_load_program( const char* file_name, const char* section_name, @@ -343,123 +179,92 @@ ebpf_api_load_program( ebpf_code_buffer_t byte_code(MAX_CODE_SIZE); size_t byte_code_size = byte_code.size(); ebpf_protocol_buffer_t request_buffer; - struct ubpf_vm* vm = nullptr; - uint64_t log_function_address; - ebpf_operation_load_code_request_t* request = nullptr; uint32_t error_message_size = 0; std::vector handles; - uint32_t result; + ebpf_result_t result = EBPF_SUCCESS; + ebpf_program_load_info load_info = {0}; + std::vector handle_map; *handle = 0; *error_message = nullptr; clear_map_descriptors(); - ebpf_verifier_options_t verifier_options{false, false, false, false, false}; - if (load_byte_code( - file_name, - section_name, - &verifier_options, - byte_code.data(), - &byte_code_size, - &program_type, - error_message) != 0) { - result = ERROR_INVALID_PARAMETER; - goto Done; - } - - // TODO: (issue #169): Should switch this to more idiomatic C++ - // Note: This leaks the program handle on some errors. - result = _create_program(program_type, file_name, section_name, &program_handle); - if (result != ERROR_SUCCESS) - goto Done; - - // Verify code. - if (verify_byte_code(&program_type, byte_code.data(), byte_code_size, error_message, &error_message_size) != 0) { - result = ERROR_INVALID_PARAMETER; - goto Done; - } - - if (get_map_descriptor_size() > *count_of_map_handles) { - result = ERROR_INSUFFICIENT_BUFFER; - goto Done; - } - - *count_of_map_handles = 0; - handles = get_all_map_handles(); - for (const auto& map_handle : handles) { - map_handles[*count_of_map_handles] = reinterpret_cast(map_handle); - (*count_of_map_handles)++; - } - - byte_code.resize(byte_code_size); - result = resolve_maps_in_byte_code(program_handle, byte_code); - if (result != ERROR_SUCCESS) { - goto Done; - } - - result = resolve_ec_function(EBPF_EC_FUNCTION_LOG, &log_function_address); - if (result != ERROR_SUCCESS) - goto Done; - - if (execution_type == EBPF_EXECUTION_JIT) { - std::map helper_id_to_adddress; - result = build_helper_id_to_address_map(program_handle, byte_code, helper_id_to_adddress); - if (result != ERROR_SUCCESS) - goto Done; - - ebpf_code_buffer_t machine_code(MAX_CODE_SIZE); - size_t machine_code_size = machine_code.size(); - - // JIT code. - vm = ubpf_create(); - if (vm == nullptr) - goto Done; - - for (const auto& helper : helper_id_to_adddress) { - if (ubpf_register(vm, helper.first, nullptr, reinterpret_cast(helper.second)) < 0) - goto Done; - } - - ubpf_set_error_print( - vm, reinterpret_cast(log_function_address)); - - if (ubpf_load( - vm, byte_code.data(), static_cast(byte_code.size()), const_cast(error_message)) < 0) { - result = ERROR_INVALID_PARAMETER; + try { + ebpf_verifier_options_t verifier_options{false, false, false, false, false}; + if (load_byte_code( + file_name, + section_name, + &verifier_options, + byte_code.data(), + &byte_code_size, + &program_type, + error_message) != 0) { + result = EBPF_INVALID_ARGUMENT; goto Done; } - if (ubpf_translate(vm, machine_code.data(), &machine_code_size, const_cast(error_message))) { - result = ERROR_INVALID_PARAMETER; + byte_code.resize(byte_code_size); + + // TODO: (issue #169): Should switch this to more idiomatic C++ + // Note: This leaks the program handle on some errors. + result = _create_program(program_type, file_name, section_name, &program_handle); + if (result != EBPF_SUCCESS) { goto Done; } - machine_code.resize(machine_code_size); - byte_code = machine_code; - } - request_buffer.resize(offsetof(ebpf_operation_load_code_request_t, code) + byte_code.size()); - request = reinterpret_cast(request_buffer.data()); - request->header.id = ebpf_operation_id_t::EBPF_OPERATION_LOAD_CODE; - request->header.length = static_cast(request_buffer.size()); - request->program_handle = reinterpret_cast(program_handle); - request->code_type = execution_type == EBPF_EXECUTION_JIT ? EBPF_CODE_NATIVE : EBPF_CODE_EBPF; + if (get_map_descriptor_size() > *count_of_map_handles) { + result = EBPF_ERROR_INSUFFICIENT_BUFFER; + goto Done; + } - std::copy( - byte_code.begin(), - byte_code.end(), - request_buffer.begin() + offsetof(ebpf_operation_load_code_request_t, code)); + // populate load_info. + load_info.file_name = const_cast(file_name); + load_info.section_name = const_cast(section_name); + load_info.program_name = nullptr; + load_info.program_type = program_type; + load_info.program_handle = program_handle; + load_info.execution_type = execution_type; + load_info.byte_code = byte_code.data(); + load_info.byte_code_size = (uint32_t)byte_code_size; + load_info.execution_context = execution_context_kernel_mode; + load_info.map_count = (uint32_t)get_map_descriptor_size(); - result = invoke_ioctl(device_handle, request_buffer); + if (load_info.map_count > 0) { + auto descriptors = get_all_map_descriptors(); + for (const auto& descriptor : descriptors) { + handle_map.emplace_back( + descriptor.ebpf_map_descriptor.original_fd, reinterpret_cast(descriptor.handle)); + } - if (result != ERROR_SUCCESS) + load_info.handle_map = handle_map.data(); + } + + result = ebpf_rpc_load_program(&load_info, error_message, &error_message_size); + if (result != EBPF_SUCCESS) { + goto Done; + } + + // Program is verified and loaded. + *count_of_map_handles = 0; + handles = get_all_map_handles(); + for (const auto& map_handle : handles) { + map_handles[*count_of_map_handles] = reinterpret_cast(map_handle); + (*count_of_map_handles)++; + } + + *handle = program_handle; + program_handle = INVALID_HANDLE_VALUE; + } catch (const std::bad_alloc&) { + result = EBPF_NO_MEMORY; goto Done; - - *handle = program_handle; - program_handle = INVALID_HANDLE_VALUE; + } catch (...) { + result = EBPF_FAILED; + goto Done; + } Done: - if (result != ERROR_SUCCESS) { + if (result != EBPF_SUCCESS) { handles = get_all_map_handles(); for (const auto& map_handle : handles) { ebpf_api_close_handle((ebpf_handle_t)map_handle); @@ -467,11 +272,9 @@ Done: } clear_map_descriptors(); - if (vm) - ubpf_destroy(vm); - - if (program_handle != INVALID_HANDLE_VALUE) + if (program_handle != INVALID_HANDLE_VALUE) { ebpf_api_close_handle(program_handle); + } return result; } @@ -492,7 +295,7 @@ ebpf_api_pin_object(ebpf_handle_t handle, const uint8_t* name, uint32_t name_len request->header.length = static_cast(request_buffer.size()); request->handle = reinterpret_cast(handle); std::copy(name, name + name_length, request->name); - return invoke_ioctl(device_handle, request_buffer); + return invoke_ioctl(request_buffer); } uint32_t @@ -505,7 +308,7 @@ ebpf_api_unpin_object(const uint8_t* name, uint32_t name_length) request->header.length = static_cast(request_buffer.size()); request->handle = UINT64_MAX; std::copy(name, name + name_length, request->name); - return invoke_ioctl(device_handle, request_buffer); + return invoke_ioctl(request_buffer); } uint32_t @@ -518,7 +321,7 @@ ebpf_api_get_pinned_map(const uint8_t* name, uint32_t name_length, ebpf_handle_t request->header.id = EBPF_OPERATION_GET_PINNING; request->header.length = static_cast(request_buffer.size()); std::copy(name, name + name_length, request->name); - auto result = invoke_ioctl(device_handle, request_buffer, reply); + auto result = invoke_ioctl(request_buffer, reply); if (result != ERROR_SUCCESS) { return result; } @@ -546,7 +349,7 @@ ebpf_api_map_find_element( request->handle = reinterpret_cast(handle); std::copy(key, key + key_size, request->key); - auto retval = invoke_ioctl(device_handle, request_buffer, reply_buffer); + auto retval = invoke_ioctl(request_buffer, reply_buffer); if (reply->header.id != ebpf_operation_id_t::EBPF_OPERATION_MAP_FIND_ELEMENT) { return ERROR_INVALID_PARAMETER; @@ -572,7 +375,7 @@ ebpf_api_map_update_element( std::copy(key, key + key_size, request->data); std::copy(value, value + value_size, request->data + key_size); - return invoke_ioctl(device_handle, request_buffer); + return invoke_ioctl(request_buffer); } uint32_t @@ -586,7 +389,7 @@ ebpf_api_map_delete_element(ebpf_handle_t handle, uint32_t key_size, const uint8 request->handle = (uint64_t)handle; std::copy(key, key + key_size, request->key); - return invoke_ioctl(device_handle, request_buffer); + return invoke_ioctl(request_buffer); } uint32_t @@ -606,7 +409,7 @@ ebpf_api_get_next_map_key(ebpf_handle_t handle, uint32_t key_size, const uint8_t request->header.length = offsetof(ebpf_operation_map_get_next_key_request_t, previous_key); } - auto retval = invoke_ioctl(device_handle, request_buffer, reply_buffer); + auto retval = invoke_ioctl(request_buffer, reply_buffer); if (reply->header.id != ebpf_operation_id_t::EBPF_OPERATION_MAP_GET_NEXT_KEY) { return ERROR_INVALID_PARAMETER; @@ -626,7 +429,7 @@ ebpf_api_get_next_map(ebpf_handle_t previous_handle, ebpf_handle_t* next_handle) _ebpf_operation_get_next_map_reply reply; - uint32_t retval = invoke_ioctl(device_handle, request, reply); + uint32_t retval = invoke_ioctl(request, reply); if (retval == ERROR_SUCCESS) { *next_handle = reinterpret_cast(reply.next_handle); } @@ -636,21 +439,20 @@ ebpf_api_get_next_map(ebpf_handle_t previous_handle, ebpf_handle_t* next_handle) uint32_t ebpf_api_get_next_program(ebpf_handle_t previous_handle, ebpf_handle_t* next_handle) { - _ebpf_operation_get_next_program_request request{ - sizeof(request), - ebpf_operation_id_t::EBPF_OPERATION_GET_NEXT_PROGRAM, - reinterpret_cast(previous_handle)}; + _ebpf_operation_get_next_program_request request{sizeof(request), + ebpf_operation_id_t::EBPF_OPERATION_GET_NEXT_PROGRAM, + reinterpret_cast(previous_handle)}; _ebpf_operation_get_next_program_reply reply; - uint32_t retval = invoke_ioctl(device_handle, request, reply); + uint32_t retval = invoke_ioctl(request, reply); if (retval == ERROR_SUCCESS) { *next_handle = reinterpret_cast(reply.next_handle); } return retval; } -uint32_t +ebpf_result_t ebpf_api_map_query_definition( ebpf_handle_t handle, uint32_t* size, @@ -659,20 +461,7 @@ ebpf_api_map_query_definition( uint32_t* value_size, uint32_t* max_entries) { - _ebpf_operation_query_map_definition_request request{ - sizeof(request), ebpf_operation_id_t::EBPF_OPERATION_QUERY_MAP_DEFINITION, reinterpret_cast(handle)}; - - _ebpf_operation_query_map_definition_reply reply; - - uint32_t retval = invoke_ioctl(device_handle, request, reply); - if (retval == ERROR_SUCCESS) { - *size = reply.map_definition.size; - *type = reply.map_definition.type; - *key_size = reply.map_definition.key_size; - *value_size = reply.map_definition.value_size; - *max_entries = reply.map_definition.max_entries; - } - return retval; + return query_map_definition(handle, size, type, key_size, value_size, max_entries); } uint32_t @@ -687,7 +476,7 @@ ebpf_api_program_query_information( auto reply = reinterpret_cast<_ebpf_operation_query_program_information_reply*>(reply_buffer.data()); - uint32_t retval = invoke_ioctl(device_handle, request, reply_buffer); + uint32_t retval = invoke_ioctl(request, reply_buffer); if (retval != ERROR_SUCCESS) { return retval; } @@ -723,7 +512,7 @@ ebpf_api_link_program(ebpf_handle_t program_handle, ebpf_attach_type_t attach_ty sizeof(request), EBPF_OPERATION_LINK_PROGRAM, reinterpret_cast(program_handle), attach_type}; ebpf_operation_link_program_reply_t reply; - uint32_t retval = invoke_ioctl(device_handle, request, reply); + uint32_t retval = invoke_ioctl(request, reply); if (retval != ERROR_SUCCESS) { return retval; } @@ -742,5 +531,5 @@ ebpf_api_close_handle(ebpf_handle_t handle) ebpf_operation_close_handle_request_t request = { sizeof(request), EBPF_OPERATION_CLOSE_HANDLE, reinterpret_cast(handle)}; - return invoke_ioctl(device_handle, request); + return invoke_ioctl(request); } diff --git a/tests/client/rpc_client.h b/libs/api/rpc_client.h similarity index 61% rename from tests/client/rpc_client.h rename to libs/api/rpc_client.h index 624973da0..61e649963 100644 --- a/tests/client/rpc_client.h +++ b/libs/api/rpc_client.h @@ -12,5 +12,8 @@ initialize_rpc_binding(void); RPC_STATUS clean_up_rpc_binding(void); -int -ebpf_rpc_verify_program(ebpf_program_verify_info* info, const char** logs, uint32_t* logs_size); +ebpf_result_t +ebpf_rpc_verify_program(ebpf_program_verify_info* info, const char** logs, uint32_t* logs_size) noexcept; + +ebpf_result_t +ebpf_rpc_load_program(ebpf_program_load_info* info, const char** logs, uint32_t* logs_size) noexcept; diff --git a/libs/api/windows_platform.cpp b/libs/api/windows_platform.cpp index c5a5e11b7..b8d6e5e02 100644 --- a/libs/api/windows_platform.cpp +++ b/libs/api/windows_platform.cpp @@ -32,7 +32,7 @@ create_map_windows( if (options.mock_map_fds) { EbpfMapType type = get_map_type_windows(map_type); fd = create_map_crab(type, key_size, value_size, max_entries, options); - cache_map_file_descriptor(map_type, key_size, value_size, fd); + cache_map_file_descriptor(map_type, key_size, value_size, max_entries, fd); return fd; } diff --git a/libs/api_common/api_common.cpp b/libs/api_common/api_common.cpp index a9c76cfe7..5011b49bb 100644 --- a/libs/api_common/api_common.cpp +++ b/libs/api_common/api_common.cpp @@ -5,6 +5,10 @@ #include #include #include +#include "api_common.hpp" +#include "device_helper.hpp" +#include "ebpf_protocol.h" +#include "ebpf_result.h" #pragma warning(push) #pragma warning(disable : 4100) // 'identifier' : unreferenced formal parameter #pragma warning(disable : 4244) // 'conversion' conversion from 'type1' to @@ -14,7 +18,7 @@ #pragma warning(pop) const char* -allocate_error_string(const std::string& str, uint32_t* length = nullptr) +allocate_error_string(const std::string& str, uint32_t* length) noexcept { char* error_message; size_t error_message_length = str.size() + 1; @@ -36,7 +40,7 @@ convert_ebpf_program_to_bytes(const std::vector& instructions) } int -get_file_size(const char* filename, size_t* byte_code_size) +get_file_size(const char* filename, size_t* byte_code_size) noexcept { int result = 0; *byte_code_size = NULL; @@ -49,3 +53,29 @@ get_file_size(const char* filename, size_t* byte_code_size) return result; } + +ebpf_result_t +query_map_definition( + ebpf_handle_t handle, + uint32_t* size, + uint32_t* type, + uint32_t* key_size, + uint32_t* value_size, + uint32_t* max_entries) noexcept +{ + _ebpf_operation_query_map_definition_request request{ + sizeof(request), ebpf_operation_id_t::EBPF_OPERATION_QUERY_MAP_DEFINITION, reinterpret_cast(handle)}; + + _ebpf_operation_query_map_definition_reply reply; + + uint32_t result = invoke_ioctl(request, reply); + if (result == ERROR_SUCCESS) { + *size = reply.map_definition.size; + *type = reply.map_definition.type; + *key_size = reply.map_definition.key_size; + *value_size = reply.map_definition.value_size; + *max_entries = reply.map_definition.max_entries; + } + + return windows_error_to_ebpf_result(result); +} diff --git a/libs/api_common/api_common.hpp b/libs/api_common/api_common.hpp index 55bdb21f6..d020f713f 100644 --- a/libs/api_common/api_common.hpp +++ b/libs/api_common/api_common.hpp @@ -7,34 +7,53 @@ #include #include "ebpf_api.h" #include "ebpf_execution_context.h" +#include "ebpf_platform.h" +#include "ebpf_result.h" #undef VOID -#include "ebpf_helpers.h" #include "platform.hpp" -extern "C" -{ -#include "ubpf.h" -} typedef struct _map_cache { uintptr_t handle; EbpfMapDescriptor ebpf_map_descriptor; + + _map_cache() {} + + _map_cache(uintptr_t handle, EbpfMapDescriptor descriptor) : handle(handle), ebpf_map_descriptor(descriptor) {} + + _map_cache( + uintptr_t handle, + int original_fd, + uint32_t type, + unsigned int key_size, + unsigned int value_size, + unsigned int max_entries, + unsigned int inner_map_fd) + : handle(handle) + { + ebpf_map_descriptor.original_fd = original_fd; + ebpf_map_descriptor.type = type; + ebpf_map_descriptor.key_size = key_size; + ebpf_map_descriptor.value_size = value_size; + ebpf_map_descriptor.max_entries = max_entries; + ebpf_map_descriptor.inner_map_fd = inner_map_fd; + } } map_cache_t; const char* -allocate_error_string(const std::string& str, uint32_t* length = nullptr); +allocate_error_string(const std::string& str, uint32_t* length = nullptr) noexcept; std::vector convert_ebpf_program_to_bytes(const std::vector& instructions); int -get_file_size(const char* filename, size_t* byte_code_size); +get_file_size(const char* filename, size_t* byte_code_size) noexcept; EbpfHelperPrototype get_helper_prototype_windows(unsigned int n); int -cache_map_handle(uint64_t handle, uint32_t type, uint32_t key_size, uint32_t value_size); +cache_map_handle(uint64_t handle, uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries); size_t get_map_descriptor_size(void); @@ -44,3 +63,70 @@ get_map_handle(int map_fd); std::vector get_all_map_handles(void); + +std::vector +get_all_map_descriptors(); + +__forceinline ebpf_result_t +windows_error_to_ebpf_result(uint32_t error) +{ + ebpf_result_t result; + + switch (error) { + case ERROR_SUCCESS: + result = EBPF_SUCCESS; + break; + + case ERROR_OUTOFMEMORY: + case ERROR_NOT_ENOUGH_MEMORY: + result = EBPF_NO_MEMORY; + break; + + case ERROR_NOT_FOUND: + result = EBPF_ERROR_NOT_FOUND; + break; + + case ERROR_INVALID_PARAMETER: + result = EBPF_INVALID_ARGUMENT; + break; + + case ERROR_NO_MORE_ITEMS: + result = EBPF_ERROR_NO_MORE_KEYS; + break; + + case ERROR_INVALID_HANDLE: + result = EBPF_ERROR_INVALID_HANDLE; + break; + + case ERROR_NOT_SUPPORTED: + result = EBPF_ERROR_NOT_SUPPORTED; + break; + + case ERROR_MORE_DATA: + result = EBPF_ERROR_INSUFFICIENT_BUFFER; + break; + + case ERROR_FILE_NOT_FOUND: + result = EBPF_FILE_NOT_FOUND; + break; + + case ERROR_ALREADY_INITIALIZED: + result = EBPF_ALREADY_INITIALIZED; + break; + + default: + result = EBPF_FAILED; + break; + } + + return result; +} + +ebpf_result_t +query_map_definition( + ebpf_handle_t handle, + uint32_t* size, + uint32_t* type, + uint32_t* key_size, + uint32_t* value_size, + uint32_t* max_entries) noexcept; \ No newline at end of file diff --git a/libs/api_common/api_common.vcxproj b/libs/api_common/api_common.vcxproj index b71b27e00..155d61211 100644 --- a/libs/api_common/api_common.vcxproj +++ b/libs/api_common/api_common.vcxproj @@ -169,6 +169,7 @@ + @@ -178,7 +179,7 @@ - + diff --git a/libs/api_common/api_common.vcxproj.filters b/libs/api_common/api_common.vcxproj.filters index cdd527c21..7e2e67c8d 100644 --- a/libs/api_common/api_common.vcxproj.filters +++ b/libs/api_common/api_common.vcxproj.filters @@ -28,6 +28,9 @@ Source Files + + Source Files + @@ -42,7 +45,7 @@ Header Files - + Header Files diff --git a/libs/api_common/device_helper.cpp b/libs/api_common/device_helper.cpp new file mode 100644 index 000000000..2e9f641f8 --- /dev/null +++ b/libs/api_common/device_helper.cpp @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +// Contains code to manage device for kernel mode execution context. + +#include +#include +#include +#include "api_common.hpp" +#include "ebpf_api.h" +#include "ebpf_bind_program_data.h" +#include "ebpf_platform.h" +#include "ebpf_program_types.h" +#include "ebpf_protocol.h" +#include "ebpf_result.h" +#include "ebpf_xdp_program_data.h" +#include "platform.h" +#undef VOID +#include "platform.hpp" + +static ebpf_handle_t _device_handle = INVALID_HANDLE_VALUE; +static std::mutex _mutex; + +ebpf_result_t +initialize_device_handle() +{ + std::scoped_lock lock(_mutex); + + if (_device_handle != INVALID_HANDLE_VALUE) { + return EBPF_ALREADY_INITIALIZED; + } + + _device_handle = Platform::CreateFile( + EBPF_DEVICE_WIN32_NAME, + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if (_device_handle == INVALID_HANDLE_VALUE) { + return windows_error_to_ebpf_result(GetLastError()); + } + + return EBPF_SUCCESS; +} + +void +clean_up_device_handle() +{ + std::scoped_lock lock(_mutex); + + if (_device_handle != INVALID_HANDLE_VALUE) { + Platform::CloseHandle(_device_handle); + _device_handle = INVALID_HANDLE_VALUE; + } +} + +ebpf_handle_t +get_device_handle() +{ + if (_device_handle == INVALID_HANDLE_VALUE) { + initialize_device_handle(); + } + + return _device_handle; +} diff --git a/libs/api_common/windows_helpers.hpp b/libs/api_common/device_helper.hpp similarity index 89% rename from libs/api_common/windows_helpers.hpp rename to libs/api_common/device_helper.hpp index 9ef79be28..449258157 100644 --- a/libs/api_common/windows_helpers.hpp +++ b/libs/api_common/device_helper.hpp @@ -3,6 +3,9 @@ #pragma once +#include "ebpf_api.h" +#include "platform.h" + // Device type #define EBPF_IOCTL_TYPE FILE_DEVICE_NETWORK @@ -18,9 +21,18 @@ typedef struct empty_reply static empty_reply_t _empty_reply; +ebpf_result_t +initialize_device_handle(); + +void +clean_up_device_handle(); + +ebpf_handle_t +get_device_handle(); + template uint32_t -invoke_ioctl(ebpf_handle_t handle, request_t& request, reply_t& reply = _empty_reply) +invoke_ioctl(request_t& request, reply_t& reply = _empty_reply) { uint32_t actual_reply_size; uint32_t request_size; @@ -56,7 +68,7 @@ invoke_ioctl(ebpf_handle_t handle, request_t& request, reply_t& reply = _empty_r } auto result = Platform::DeviceIoControl( - handle, + get_device_handle(), IOCTL_EBPFCTL_METHOD_BUFFERED, request_ptr, request_size, @@ -75,5 +87,3 @@ invoke_ioctl(ebpf_handle_t handle, request_t& request, reply_t& reply = _empty_r return ERROR_SUCCESS; } - -extern ebpf_handle_t device_handle; diff --git a/libs/api_common/map_descriptors.cpp b/libs/api_common/map_descriptors.cpp index 54608300b..92cc20483 100644 --- a/libs/api_common/map_descriptors.cpp +++ b/libs/api_common/map_descriptors.cpp @@ -11,9 +11,7 @@ cache_map_file_descriptors(const EbpfMapDescriptor* map_descriptors, uint32_t ma { for (uint32_t i = 0; i < map_descriptors_count; i++) { auto descriptor = map_descriptors[i]; - _map_file_descriptors.push_back( - {(uintptr_t)descriptor.original_fd, - {descriptor.original_fd, descriptor.type, descriptor.key_size, descriptor.value_size, 0}}); + _map_file_descriptors.emplace_back((uintptr_t)descriptor.original_fd, descriptor); } } @@ -33,7 +31,7 @@ get_map_cache_entry(uint64_t map_fd) } } - return _map_file_descriptors[0]; + throw std::runtime_error(std::string("Map cache entry for map fd ") + std::to_string(map_fd) + " not found."); } EbpfMapDescriptor& @@ -72,19 +70,32 @@ get_all_map_handles() return handles; } -void -cache_map_file_descriptor(uint32_t type, uint32_t key_size, uint32_t value_size, int fd) +std::vector +get_all_map_descriptors() { - _map_file_descriptors.push_back({(uintptr_t)fd, {fd, type, key_size, value_size, 0}}); + return _map_file_descriptors; +} + +void +cache_map_file_descriptor(uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, int fd) +{ + _map_file_descriptors.emplace_back((uintptr_t)fd, fd, type, key_size, value_size, max_entries, 0); +} + +void +cache_map_file_descriptor_with_handle( + uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, int fd, uintptr_t handle) +{ + _map_file_descriptors.emplace_back(handle, fd, type, key_size, value_size, max_entries, 0); } int -cache_map_handle(uint64_t handle, uint32_t type, uint32_t key_size, uint32_t value_size) +cache_map_handle(uint64_t handle, uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries) { // TODO: Replace this with the CRT helper to create FD from handle once we // have real handles. int fd = static_cast(_map_file_descriptors.size() + 1); - _map_file_descriptors.push_back({handle, {fd, type, key_size, value_size, 0}}); + _map_file_descriptors.emplace_back(handle, fd, type, key_size, value_size, max_entries, 0); return static_cast(_map_file_descriptors.size()); } diff --git a/libs/api_common/map_descriptors.hpp b/libs/api_common/map_descriptors.hpp index db3ea000b..d09ecb2fe 100644 --- a/libs/api_common/map_descriptors.hpp +++ b/libs/api_common/map_descriptors.hpp @@ -12,7 +12,11 @@ void cache_map_file_descriptors(const EbpfMapDescriptor* map_descriptors, uint32_t map_descriptors_count); void -cache_map_file_descriptor(uint32_t type, uint32_t key_size, uint32_t value_size, int fd); +cache_map_file_descriptor(uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, int fd); + +void +cache_map_file_descriptor_with_handle( + uint32_t type, uint32_t key_size, uint32_t value_size, uint32_t max_entries, int fd, uintptr_t handle); void clear_map_descriptors(void); @@ -22,3 +26,6 @@ get_map_descriptor_at_index(int index); uintptr_t get_map_handle_at_index(size_t index); + +void +clear_program_information_cache(); \ No newline at end of file diff --git a/libs/api_common/windows_helpers.cpp b/libs/api_common/windows_helpers.cpp index aa24f51bc..3497ed6a3 100644 --- a/libs/api_common/windows_helpers.cpp +++ b/libs/api_common/windows_helpers.cpp @@ -3,7 +3,9 @@ #include #include -#include "ebpf_api.h" +#include +#include +#include "device_helper.hpp" #include "ebpf_bind_program_data.h" #include "ebpf_platform.h" #include "ebpf_program_types.h" @@ -13,7 +15,6 @@ #include "platform.h" #undef VOID #include "platform.hpp" -#include "windows_helpers.hpp" struct guid_compare { @@ -24,9 +25,13 @@ struct guid_compare } }; -thread_local std::map g_program_information_cache; +static thread_local std::map _program_information_cache; -ebpf_handle_t device_handle = INVALID_HANDLE_VALUE; +void +clear_program_information_cache() +{ + _program_information_cache.clear(); +} uint32_t get_program_information_data(ebpf_program_type_t program_type, ebpf_extension_data_t** program_information_data) @@ -36,7 +41,7 @@ get_program_information_data(ebpf_program_type_t program_type, ebpf_extension_da sizeof(request), ebpf_operation_id_t::EBPF_OPERATION_GET_PROGRAM_INFORMATION, program_type}; auto reply = reinterpret_cast(reply_buffer.data()); - uint32_t retval = invoke_ioctl(device_handle, request, reply_buffer); + uint32_t retval = invoke_ioctl(request, reply_buffer); if (retval != ERROR_SUCCESS) { return retval; } @@ -70,8 +75,8 @@ get_program_type_info(const ebpf_program_information_t** info) size_t encoded_data_size = 0; // See if we already have the program information cached. - auto it = g_program_information_cache.find(*program_type); - if (it == g_program_information_cache.end()) { + auto it = _program_information_cache.find(*program_type); + if (it == _program_information_cache.end()) { // Try to query the information from the execution context. ebpf_extension_data_t* program_information_data; uint32_t error = get_program_information_data(*program_type, &program_information_data); @@ -99,10 +104,10 @@ get_program_type_info(const ebpf_program_information_t** info) return result; } - g_program_information_cache[*program_type] = ebpf_helper::ebpf_memory_ptr(program_information); + _program_information_cache[*program_type] = ebpf_helper::ebpf_memory_ptr(program_information); } - *info = (const ebpf_program_information_t*)g_program_information_cache[*program_type].get(); + *info = (const ebpf_program_information_t*)_program_information_cache[*program_type].get(); return EBPF_SUCCESS; } diff --git a/libs/service/api_service.cpp b/libs/service/api_service.cpp index 108549a78..ad28dc022 100644 --- a/libs/service/api_service.cpp +++ b/libs/service/api_service.cpp @@ -4,9 +4,8 @@ #include #include #include "api_common.hpp" -#include "api_internal.h" #include "api_service.h" -#include "ebpf_api.h" +#include "device_helper.hpp" #include "ebpf_platform.h" #include "ebpf_protocol.h" #include "map_descriptors.hpp" @@ -17,7 +16,189 @@ extern "C" } #include "Verifier.h" #include "verifier_service.h" -#include "windows_helpers.hpp" + +#define MAX_CODE_SIZE_IN_BYTES (32 * 1024) // 32 KB + +static ebpf_result_t +_build_helper_id_to_address_map( + ebpf_handle_t program_handle, ebpf_code_buffer_t& byte_code, std::map& helper_id_to_adddress) +{ + ebpf_inst* instructions = reinterpret_cast(byte_code.data()); + for (size_t index = 0; index < byte_code.size() / sizeof(ebpf_inst); index++) { + ebpf_inst& instruction = instructions[index]; + if (instruction.opcode != INST_OP_CALL) { + continue; + } + helper_id_to_adddress[instruction.imm] = 0; + } + + if (helper_id_to_adddress.size() == 0) + return EBPF_SUCCESS; + + ebpf_protocol_buffer_t request_buffer( + offsetof(ebpf_operation_resolve_helper_request_t, helper_id) + sizeof(uint32_t) * helper_id_to_adddress.size()); + + ebpf_protocol_buffer_t reply_buffer( + offsetof(ebpf_operation_resolve_helper_reply_t, address) + sizeof(uint64_t) * helper_id_to_adddress.size()); + + auto request = reinterpret_cast(request_buffer.data()); + auto reply = reinterpret_cast(reply_buffer.data()); + request->header.id = ebpf_operation_id_t::EBPF_OPERATION_RESOLVE_HELPER; + request->header.length = static_cast(request_buffer.size()); + request->program_handle = reinterpret_cast(program_handle); + + size_t index = 0; + for (const auto& [helper_id, address] : helper_id_to_adddress) { + request->helper_id[index++] = helper_id; + } + + uint32_t result = invoke_ioctl(request_buffer, reply_buffer); + if (result != ERROR_SUCCESS) { + return windows_error_to_ebpf_result(result); + } + + index = 0; + for (auto& [helper_id, address] : helper_id_to_adddress) { + address = reply->address[index++]; + } + + return EBPF_SUCCESS; +} + +static ebpf_result_t +_resolve_ec_function(ebpf_ec_function_t function, uint64_t* address) +{ + ebpf_operation_get_ec_function_request_t request = {sizeof(request), EBPF_OPERATION_GET_EC_FUNCTION, function}; + ebpf_operation_get_ec_function_reply_t reply; + + uint32_t result = invoke_ioctl(request, reply); + if (result != ERROR_SUCCESS) { + return windows_error_to_ebpf_result(result); + } + + if (reply.header.id != ebpf_operation_id_t::EBPF_OPERATION_GET_EC_FUNCTION) { + return EBPF_INVALID_ARGUMENT; + } + + *address = reply.address; + + return EBPF_SUCCESS; +} + +static ebpf_result_t +_resolve_maps_in_byte_code(ebpf_handle_t program_handle, ebpf_code_buffer_t& byte_code) +{ + std::vector instruction_offsets; + std::vector map_handles; + + ebpf_inst* instructions = reinterpret_cast(byte_code.data()); + ebpf_inst* instruction_end = reinterpret_cast(byte_code.data() + byte_code.size()); + for (size_t index = 0; index < byte_code.size() / sizeof(ebpf_inst); index++) { + ebpf_inst& first_instruction = instructions[index]; + ebpf_inst& second_instruction = instructions[index + 1]; + if (first_instruction.opcode != INST_OP_LDDW_IMM) { + continue; + } + if (&instructions[index + 1] >= instruction_end) { + return EBPF_INVALID_ARGUMENT; + } + index++; + + // Check for LD_MAP flag + if (first_instruction.src != 1) { + continue; + } + + uint64_t imm = + static_cast(first_instruction.imm) | (static_cast(second_instruction.imm) << 32); + instruction_offsets.push_back(index - 1); + map_handles.push_back(imm); + } + + if (map_handles.empty()) { + return EBPF_SUCCESS; + } + + ebpf_protocol_buffer_t request_buffer( + offsetof(ebpf_operation_resolve_map_request_t, map_handle) + sizeof(uint64_t) * map_handles.size()); + + ebpf_protocol_buffer_t reply_buffer( + offsetof(ebpf_operation_resolve_map_reply_t, address) + sizeof(uint64_t) * map_handles.size()); + + auto request = reinterpret_cast(request_buffer.data()); + auto reply = reinterpret_cast(reply_buffer.data()); + request->header.id = ebpf_operation_id_t::EBPF_OPERATION_RESOLVE_MAP; + request->header.length = static_cast(request_buffer.size()); + request->program_handle = reinterpret_cast(program_handle); + + for (size_t index = 0; index < map_handles.size(); index++) { + if (map_handles[index] > get_map_descriptor_size()) { + return EBPF_ERROR_INVALID_HANDLE; + } + request->map_handle[index] = get_map_handle_at_index((int)map_handles[index] - 1); + } + + uint32_t result = invoke_ioctl(request_buffer, reply_buffer); + if (result != ERROR_SUCCESS) { + return windows_error_to_ebpf_result(result); + } + + for (size_t index = 0; index < map_handles.size(); index++) { + ebpf_inst& first_instruction = instructions[instruction_offsets[index]]; + ebpf_inst& second_instruction = instructions[instruction_offsets[index] + 1]; + + // Clear LD_MAP flag + first_instruction.src = 0; + + // Replace handle with address + uint64_t new_imm = reply->address[index]; + first_instruction.imm = static_cast(new_imm); + second_instruction.imm = static_cast(new_imm >> 32); + } + + return EBPF_SUCCESS; +} + +static ebpf_result_t +_query_and_cache_map_descriptors(fd_handle_map* handle_map, uint32_t handle_map_count) +{ + ebpf_result_t result; + EbpfMapDescriptor descriptor; + + if (handle_map_count > 0) { + for (uint32_t i = 0; i < handle_map_count; i++) { + uint32_t size; + descriptor = {0}; + result = query_map_definition( + handle_map[i].handle, + &size, + &descriptor.type, + &descriptor.key_size, + &descriptor.value_size, + &descriptor.max_entries); + if (result != EBPF_SUCCESS) { + return result; + } + + cache_map_file_descriptor_with_handle( + descriptor.type, + descriptor.key_size, + descriptor.value_size, + descriptor.max_entries, + handle_map[i].file_descriptor, + (uintptr_t)handle_map[i].handle); + } + } + + return EBPF_SUCCESS; +} + +void +ebpf_clear_thread_local_storage() noexcept +{ + clear_map_descriptors(); + clear_program_information_cache(); +} ebpf_result_t ebpf_verify_program( @@ -28,10 +209,9 @@ ebpf_verify_program( uint32_t byte_code_size, uint8_t* byte_code, const char** logs, - uint32_t* logs_size) + uint32_t* logs_size) noexcept { ebpf_result_t result = EBPF_SUCCESS; - int error = 0; // Only kernel execution context supported currently. if (execution_context == execution_context_user_mode) { @@ -45,12 +225,8 @@ ebpf_verify_program( cache_map_file_descriptors(map_descriptors, map_descriptors_count); } - // Verify the program - error = verify_byte_code(program_type, byte_code, byte_code_size, logs, logs_size); - - if (error != 0) { - result = EBPF_VALIDATION_FAILED; - } + // Verify the program. + result = verify_byte_code(program_type, byte_code, byte_code_size, logs, logs_size); } catch (const std::bad_alloc&) { result = EBPF_NO_MEMORY; } catch (std::runtime_error& err) { @@ -64,3 +240,165 @@ ebpf_verify_program( return result; } + +ebpf_result_t +ebpf_verify_and_load_program( + const GUID* program_type, + ebpf_handle_t program_handle, + ebpf_execution_context_t execution_context, + ebpf_execution_type_t execution_type, + uint32_t handle_map_count, + fd_handle_map* handle_map, + uint32_t byte_code_size, + uint8_t* byte_code, + const char** error_message, + uint32_t* error_message_size) noexcept +{ + ebpf_result_t result = EBPF_SUCCESS; + int error = 0; + uint64_t log_function_address; + struct ubpf_vm* vm = nullptr; + ebpf_protocol_buffer_t request_buffer; + ebpf_operation_load_code_request_t* request = nullptr; + + // Only kernel execution context supported currently. + if (execution_context == execution_context_user_mode) { + return EBPF_INVALID_ARGUMENT; + } + + *error_message = nullptr; + *error_message_size = 0; + + clear_map_descriptors(); + + // Query map descriptors from execution context. + try { + result = _query_and_cache_map_descriptors(handle_map, handle_map_count); + if (result != EBPF_SUCCESS) { + goto Exit; + } + + // Verify the program + result = verify_byte_code(program_type, byte_code, byte_code_size, error_message, error_message_size); + if (result != EBPF_SUCCESS) { + goto Exit; + } + + if (byte_code_size > MAX_CODE_SIZE_IN_BYTES) { + result = EBPF_PROGRAM_TOO_LARGE; + goto Exit; + } + + ebpf_code_buffer_t byte_code_buffer(byte_code, byte_code + byte_code_size); + + result = _resolve_maps_in_byte_code(program_handle, byte_code_buffer); + if (result != EBPF_SUCCESS) { + goto Exit; + } + + result = _resolve_ec_function(EBPF_EC_FUNCTION_LOG, &log_function_address); + if (result != EBPF_SUCCESS) { + goto Exit; + } + + if (execution_type == EBPF_EXECUTION_JIT) { + std::map helper_id_to_adddress; + result = _build_helper_id_to_address_map(program_handle, byte_code_buffer, helper_id_to_adddress); + if (result != EBPF_SUCCESS) + goto Exit; + + ebpf_code_buffer_t machine_code(MAX_CODE_SIZE_IN_BYTES); + size_t machine_code_size = machine_code.size(); + + // JIT code. + vm = ubpf_create(); + if (vm == nullptr) { + result = EBPF_JIT_COMPILATION_FAILED; + goto Exit; + } + + for (const auto& [helper_id, address] : helper_id_to_adddress) { + if (ubpf_register(vm, helper_id, nullptr, reinterpret_cast(address)) < 0) { + result = EBPF_JIT_COMPILATION_FAILED; + goto Exit; + } + } + + ubpf_set_error_print( + vm, reinterpret_cast(log_function_address)); + + if (ubpf_load( + vm, + byte_code_buffer.data(), + static_cast(byte_code_buffer.size()), + const_cast(error_message)) < 0) { + result = EBPF_JIT_COMPILATION_FAILED; + goto Exit; + } + + if (ubpf_translate(vm, machine_code.data(), &machine_code_size, const_cast(error_message))) { + result = EBPF_JIT_COMPILATION_FAILED; + goto Exit; + } + machine_code.resize(machine_code_size); + byte_code_buffer = machine_code; + + if (*error_message != nullptr) { + *error_message_size = (uint32_t)strlen(*error_message); + } + } + + request_buffer.resize(offsetof(ebpf_operation_load_code_request_t, code) + byte_code_buffer.size()); + request = reinterpret_cast(request_buffer.data()); + request->header.id = ebpf_operation_id_t::EBPF_OPERATION_LOAD_CODE; + request->header.length = static_cast(request_buffer.size()); + request->program_handle = reinterpret_cast(program_handle); + request->code_type = execution_type == EBPF_EXECUTION_JIT ? EBPF_CODE_NATIVE : EBPF_CODE_EBPF; + + std::copy( + byte_code_buffer.begin(), + byte_code_buffer.end(), + request_buffer.begin() + offsetof(ebpf_operation_load_code_request_t, code)); + + error = invoke_ioctl(request_buffer); + + if (error != ERROR_SUCCESS) { + result = EBPF_PROGRAM_LOAD_FAILED; + goto Exit; + } + } catch (const std::bad_alloc&) { + result = EBPF_NO_MEMORY; + } catch (std::runtime_error& err) { + auto message = err.what(); + *error_message = allocate_error_string(message, error_message_size); + + result = EBPF_VALIDATION_FAILED; + } catch (...) { + result = EBPF_FAILED; + } + +Exit: + if (vm) { + ubpf_destroy(vm); + } + + return result; +} + +uint32_t +ebpf_service_initialize() noexcept +{ + // This is best effort. If device handle does not initialize, + // it will be re-attempted before an IOCTL call is made. + // This is needed to ensure the service can successfully start + // even if the driver is not installed. + initialize_device_handle(); + + return ERROR_SUCCESS; +} + +void +ebpf_service_cleanup() noexcept +{ + clean_up_device_handle(); +} diff --git a/libs/service/api_service.h b/libs/service/api_service.h index e34885ce7..071c49946 100644 --- a/libs/service/api_service.h +++ b/libs/service/api_service.h @@ -3,8 +3,12 @@ #pragma once +#include "api_internal.h" +#include "ebpf_api.h" #include "ebpf_execution_context.h" +#include "ebpf_execution_type.h" #include "ebpf_result.h" +#include "rpc_interface_h.h" ebpf_result_t ebpf_verify_program( @@ -15,4 +19,26 @@ ebpf_verify_program( uint32_t byte_code_size, uint8_t* byte_code, const char** logs, - uint32_t* logs_size); + uint32_t* logs_size) noexcept; + +ebpf_result_t +ebpf_verify_and_load_program( + const GUID* program_type, + ebpf_handle_t program_handle, + ebpf_execution_context_t execution_context, + ebpf_execution_type_t execution_type, + uint32_t handle_map_count, + fd_handle_map* handle_map, + uint32_t byte_code_size, + uint8_t* byte_code, + const char** error_message, + uint32_t* error_message_size) noexcept; + +uint32_t +ebpf_service_initialize() noexcept; + +void +ebpf_service_cleanup() noexcept; + +void +ebpf_clear_thread_local_storage() noexcept; diff --git a/libs/service/service.vcxproj b/libs/service/service.vcxproj index 8d429f1a6..edadf5744 100644 --- a/libs/service/service.vcxproj +++ b/libs/service/service.vcxproj @@ -131,7 +131,7 @@ true NotUsing pch.h - $(SolutionDir)libs\api_common;$(SolutionDir)libs\api;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories) + $(SolutionDir)rpc_interface;$(SolutionDir)libs\api_common;$(SolutionDir)libs\api;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories) stdcpplatest MultiThreadedDebugDLL true @@ -152,7 +152,7 @@ true NotUsing pch.h - $(SolutionDir)libs\api_common;$(SolutionDir)libs\api;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories) + $(SolutionDir)rpc_interface;$(SolutionDir)libs\api_common;$(SolutionDir)libs\api;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)external\ubpf\vm;$(SolutionDir)external\ubpf\vm\inc;$(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)external\ebpf-verifier\external;$(OutDir);%(AdditionalIncludeDirectories) stdcpplatest true diff --git a/libs/service/verifier_service.cpp b/libs/service/verifier_service.cpp index 1f918457f..82f635475 100644 --- a/libs/service/verifier_service.cpp +++ b/libs/service/verifier_service.cpp @@ -20,13 +20,13 @@ #include "tlv.h" #include "windows_platform_service.hpp" -static int -analyze(raw_program& raw_prog, const char** error_message, uint32_t* error_message_size = nullptr) +static ebpf_result_t +_analyze(raw_program& raw_prog, const char** error_message, uint32_t* error_message_size = nullptr) { std::variant prog_or_error = unmarshal(raw_prog); if (!std::holds_alternative(prog_or_error)) { *error_message = allocate_error_string(std::get(prog_or_error), error_message_size); - return 1; // Error; + return EBPF_VALIDATION_FAILED; // Error; } InstructionSeq& prog = std::get(prog_or_error); @@ -43,12 +43,12 @@ analyze(raw_program& raw_prog, const char** error_message, uint32_t* error_messa (void)ebpf_verify_program(oss, prog, raw_prog.info, &options, &stats); *error_message = allocate_error_string(oss.str(), error_message_size); - return 1; // Error; + return EBPF_VALIDATION_FAILED; // Error; } - return 0; // Success. + return EBPF_SUCCESS; // Success. } -int +ebpf_result_t verify_byte_code( const GUID* program_type, const uint8_t* byte_code, @@ -66,5 +66,5 @@ verify_byte_code( raw_program raw_prog{file, section, instructions, info}; - return analyze(raw_prog, error_message, error_message_size); + return _analyze(raw_prog, error_message, error_message_size); } diff --git a/libs/service/verifier_service.h b/libs/service/verifier_service.h index f7a311fa3..0dac1a991 100644 --- a/libs/service/verifier_service.h +++ b/libs/service/verifier_service.h @@ -7,7 +7,7 @@ #undef VOID #include "platform.hpp" -int +ebpf_result_t verify_byte_code( const GUID* program_type, const uint8_t* byte_code, diff --git a/rpc_interface/rpc_interface.idl b/rpc_interface/rpc_interface.idl index 2b5db55ab..b1687d6ee 100644 --- a/rpc_interface/rpc_interface.idl +++ b/rpc_interface/rpc_interface.idl @@ -4,6 +4,7 @@ */ import "ebpf_execution_context.h"; +import "ebpf_execution_type.h"; import "ebpf_result.h"; import "stdint.h"; import "wtypes.idl"; @@ -38,11 +39,14 @@ import "wtypes.idl"; typedef struct _ebpf_program_load_info { - // Optional file name with full path. + // Optional file name. [string] char* file_name; + // Optional section name. + [string] char* section_name; // Optional program name. [string] char* program_name; GUID program_type; + ebpf_execution_type_t execution_type; file_handle_t program_handle; ebpf_execution_context_t execution_context; uint32_t map_count; @@ -61,7 +65,7 @@ import "wtypes.idl"; [ size_is(byte_code_size), ref ] uint8_t* byte_code; } ebpf_program_verify_info; - ebpf_result_t verify_and_jit_program( + ebpf_result_t verify_and_load_program( [ in, ref ] ebpf_program_load_info * info, [ out, ref ] uint32_t * logs_size, [ out, size_is(, *logs_size), ref ] char** logs); diff --git a/rpc_interface/rpc_interface.vcxproj b/rpc_interface/rpc_interface.vcxproj index 9f40075e3..52e098375 100644 --- a/rpc_interface/rpc_interface.vcxproj +++ b/rpc_interface/rpc_interface.vcxproj @@ -130,6 +130,7 @@ $(SolutionDir)include;$(SolutionDir)libs\api;%(AdditionalIncludeDirectories) Unsigned /prefix server "ebpf_server_" /prefix client "ebpf_client_" %(AdditionalOptions) + NT100 diff --git a/tests/client/ebpf_client.vcxproj b/tests/client/ebpf_client.vcxproj index e7a8c3c8f..e70f1e994 100644 --- a/tests/client/ebpf_client.vcxproj +++ b/tests/client/ebpf_client.vcxproj @@ -152,9 +152,9 @@ + - @@ -184,8 +184,8 @@ + - diff --git a/tests/client/ebpf_client.vcxproj.filters b/tests/client/ebpf_client.vcxproj.filters index a7c0a319d..11e763679 100644 --- a/tests/client/ebpf_client.vcxproj.filters +++ b/tests/client/ebpf_client.vcxproj.filters @@ -15,9 +15,6 @@ - - Source Files - Source Files @@ -27,6 +24,9 @@ Source Files + + Source Files + @@ -38,7 +38,7 @@ Source Files - + Header Files diff --git a/tests/client/rpc_client.cpp b/tests/client/rpc_client.cpp deleted file mode 100644 index 626f78a59..000000000 --- a/tests/client/rpc_client.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation -// SPDX-License-Identifier: MIT - -#include "ebpf_api.h" -#pragma warning(push) -#pragma warning(disable : 4100) // 'identifier' : unreferenced formal parameter -#pragma warning(disable : 4244) // 'conversion' conversion from 'type1' to - // 'type2', possible loss of data -#undef VOID -#include "ebpf_verifier.hpp" -#pragma warning(pop) -#include "ebpf_windows.h" -#include "header.h" -#include "rpc_interface_c.c" - -#pragma comment(lib, "Rpcrt4.lib") - -static RPC_WSTR _string_binding = nullptr; -static const WCHAR* _protocol_sequence = L"ncacn_np"; - -#define RPC_SERVER_ENDPOINT L"\\pipe\\ebpf_service" - -int -ebpf_rpc_verify_program(ebpf_program_verify_info* info, const char** logs, uint32_t* logs_size) -{ - unsigned long code; - int result; - - RpcTryExcept { result = (int)ebpf_client_verify_program(info, logs_size, const_cast(logs)); } - RpcExcept(RpcExceptionFilter(RpcExceptionCode())) - { - code = RpcExceptionCode(); - printf("ebpf_rpc_verify_program: runtime reported exception 0x%lx = %ld\n", code, code); - result = (int)EBPF_FAILED; - } - RpcEndExcept - - printf("ebpf_rpc_verify_program: got return code %d from the server\n\n", result); - - return result; -} - -RPC_STATUS -initialize_rpc_binding() -{ - RPC_STATUS status; - RPC_WSTR uuid = nullptr; - const WCHAR* network_address = nullptr; - RPC_WSTR options = nullptr; - - status = RpcStringBindingCompose( - uuid, - (RPC_WSTR)_protocol_sequence, - (RPC_WSTR)network_address, - (RPC_WSTR)RPC_SERVER_ENDPOINT, - options, - &_string_binding); - - if (status != RPC_S_OK) { - return status; - } - - return RpcBindingFromStringBinding(_string_binding, &ebpf_service_interface_handle); -} - -RPC_STATUS -clean_up_rpc_binding() -{ - RPC_STATUS status = RpcStringFree(&_string_binding); - if (status != RPC_S_OK) { - printf("RpcStringFree failed with error %d\n", status); - } - - status = RpcBindingFree(&ebpf_service_interface_handle); - if (status != RPC_S_OK) { - printf("RpcBindingFree failed with error %d\n", status); - } - - return status; -} diff --git a/tests/end_to_end/end_to_end.vcxproj b/tests/end_to_end/end_to_end.vcxproj index fe9e6dd85..85ce99f0e 100644 --- a/tests/end_to_end/end_to_end.vcxproj +++ b/tests/end_to_end/end_to_end.vcxproj @@ -80,7 +80,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(OutDir);%(AdditionalIncludeDirectories) + $(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(OutDir);%(AdditionalIncludeDirectories) stdcpp17 true MultiThreadedDebugDLL @@ -98,7 +98,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(OutDir);%(AdditionalIncludeDirectories) + $(SolutionDir)external\ebpf-verifier\src;$(SolutionDir)libs\service;$(SolutionDir)rpc_interface;$(SolutionDir)include;$(SolutionDir)libs\platform;$(SolutionDir)libs\platform\user;$(SolutionDir)libs\execution_context;$(SolutionDir)libs\api;$(OutDir);%(AdditionalIncludeDirectories) stdcpp17 true diff --git a/tests/end_to_end/mock.cpp b/tests/end_to_end/mock.cpp index 836e95a77..eda7a5a45 100644 --- a/tests/end_to_end/mock.cpp +++ b/tests/end_to_end/mock.cpp @@ -3,8 +3,11 @@ * SPDX-License-Identifier: MIT */ -#include "mock.h" +#include "api_service.h" #include "ebpf_api.h" +#include "mock.h" +#include "rpc_interface_h.h" + std::function create_file_handler; std::function device_io_control_handler; std::function close_handle_handler; @@ -58,4 +61,30 @@ CloseHandle(_In_ _Post_ptr_invalid_ ebpf_handle_t handle) return close_handle_handler(handle); } -} // namespace Platform \ No newline at end of file +} // namespace Platform + +// RPC related mock functions. + +RPC_STATUS +initialize_rpc_binding() { return RPC_S_OK; } + +RPC_STATUS +clean_up_rpc_binding() { return RPC_S_OK; } + +ebpf_result_t +ebpf_rpc_load_program(ebpf_program_load_info* info, const char** logs, uint32_t* logs_size) +{ + // Short circuit rpc call to service lib. + + return ebpf_verify_and_load_program( + &info->program_type, + info->program_handle, + info->execution_context, + info->execution_type, + info->map_count, + info->handle_map, + info->byte_code_size, + info->byte_code, + const_cast(logs), + logs_size); +}