diff --git a/src/ebpf/include/ebpf_api.h b/src/ebpf/include/ebpf_api.h index 67e2ad071..8dace616e 100644 --- a/src/ebpf/include/ebpf_api.h +++ b/src/ebpf/include/ebpf_api.h @@ -266,7 +266,7 @@ extern "C" ebpf_api_unpin_map(const uint8_t* name, uint32_t name_length); /** - * @brief Find a map given it's associated name. + * @brief Find a map given its associated name. * @param[in] name Name to find. */ uint32_t diff --git a/src/ebpf/include/ebpf_core.h b/src/ebpf/include/ebpf_core.h index 9b2c26965..30c745dd2 100644 --- a/src/ebpf/include/ebpf_core.h +++ b/src/ebpf/include/ebpf_core.h @@ -14,6 +14,8 @@ extern "C" #endif #include "ebpf_protocol.h" + extern GUID ebpf_global_helper_function_interface_id; + typedef uint32_t(__stdcall* ebpf_hook_function)(uint8_t*); /** @@ -82,23 +84,6 @@ extern "C" ebpf_core_get_protocol_handler_properties( ebpf_operation_id_t operation_id, _Out_ size_t* minimum_request_size, _Out_ size_t* minimum_reply_size); - /** - * @brief Get the count of global helper functions. - * - * @return Count of global helper functions. - */ - size_t - ebpf_core_get_global_helper_count(); - - /** - * @brief Get the address of a global helper function. - * - * @param[in] helper_id Id of the global helper function. - * @return Address of the global helper function. - */ - const void* - ebpf_core_get_global_helper(size_t helper_id); - #ifdef __cplusplus } #endif diff --git a/src/ebpf/include/ebpf_platform.h b/src/ebpf/include/ebpf_platform.h index d1558ec96..435c9810f 100644 --- a/src/ebpf/include/ebpf_platform.h +++ b/src/ebpf/include/ebpf_platform.h @@ -42,13 +42,22 @@ extern "C" typedef struct _epbf_non_preemptible_work_item epbf_non_preemptible_work_item_t; typedef struct _ebpf_timer_work_item ebpf_timer_work_item_t; typedef struct _ebpf_extension_client ebpf_extension_client_t; + typedef struct _ebpf_extension_provider ebpf_extension_provider_t; typedef ebpf_error_code_t (*_ebpf_extension_dispatch_function)(); typedef struct _ebpf_extension_dispatch_table { - size_t size; + uint16_t version; + uint16_t size; _ebpf_extension_dispatch_function function[1]; } ebpf_extension_dispatch_table_t; + typedef struct _ebpf_extension_data + { + uint16_t version; + uint16_t size; + uint8_t data[1]; + } ebpf_extension_data_t; + #define EBPF_LOCK_SIZE sizeof(uint64_t) #define EBPF_LOCK_STATE_SIZE sizeof(uint64_t) typedef uint8_t ebpf_lock_t[EBPF_LOCK_SIZE]; @@ -425,18 +434,18 @@ extern "C" ebpf_interlocked_compare_exchange_int32(volatile int32_t* destination, int32_t exchange, int32_t comperand); /** - * @brief Load an extension and get it's dispatch table. + * @brief Load an extension and get its dispatch table. * * @param[out] client_context Context used to unload the extension. - * @param[in] client_id GUID representing the identity of the client. + * @param[in] interface_id GUID representing the identity of the interface. + * @param[in] client_binding_context Opaque per-instance pointer passed to the extension. * @param[in] client_data Opaque client data passed to the extension. * @param[in] client_data_length Length of the client data. * @param[in] client_dispatch_table Table of function pointers the client * exposes. * @param[in] provider_id GUID representing the extension to load. * @param[out] provider_data Opaque provider data. - * @param[out] provider_data_length Length of the provider data. - * @param[out] provider_dispatch_table Table of function pointer the + * @param[out] provider_dispatch_table Table of function pointers the * provider exposes. * @retval EBPF_ERROR_SUCCESS The operation was successful. * @retval EBPF_ERROR_NOT_FOUND The provider was not found. @@ -446,13 +455,12 @@ extern "C" ebpf_error_code_t ebpf_extension_load( ebpf_extension_client_t** client_context, - const GUID* client_id, - const uint8_t* client_data, - size_t client_data_length, + const GUID* interface_id, + void* client_binding_context, + const ebpf_extension_data_t* client_data, const ebpf_extension_dispatch_table_t* client_dispatch_table, - const GUID* provider_id, - uint8_t** provider_data, - size_t* provider_data_length, + void** provider_binding_context, + ebpf_extension_data_t** provider_data, ebpf_extension_dispatch_table_t** provider_dispatch_table); /** @@ -463,6 +471,51 @@ extern "C" void ebpf_extension_unload(ebpf_extension_client_t* client_context); + typedef ebpf_error_code_t (*ebpf_provider_client_attach_callback_t)( + const GUID* client_id, + void* client_binding_context, + const ebpf_extension_data_t* client_data, + const ebpf_extension_dispatch_table_t* client_dispatch_table); + + typedef ebpf_error_code_t (*ebpf_provider_client_detach_callback_t)(const GUID* client_id); + + /** + * @brief Register as an extension provider. + * + * @param[out] provider_context Context used to unload the provider. + * @param[in] interface_id GUID representing the identity of the interface. + * @param[in] provider_data Opaque provider data. + * @param[in] provider_dispatch_table Table of function pointers the + * provider exposes. + * @param[in] client_attach_callback Function invoked when a client attaches. + * @param[in] client_detach_callback Function invoked when a client detaches. + * @retval EBPF_ERROR_SUCCESS The operation was successful. + * @retval EBPF_ERROR_EXTENSION_FAILED_TO_LOAD The provider was unable to + * load. + * @retval EBPF_ERROR_OUT_OF_RESOURCES Unable to allocate resources for this + * operation. + */ + ebpf_error_code_t + ebpf_provider_load( + ebpf_extension_provider_t** provider_context, + const GUID* interface_id, + void* provider_binding_context, + const ebpf_extension_data_t* provider_data, + const ebpf_extension_dispatch_table_t* provider_dispatch_table, + ebpf_provider_client_attach_callback_t client_attach_callback, + ebpf_provider_client_detach_callback_t client_detach_callback); + + /** + * @brief Unload a provider. + * + * @param[in] provider_context Provider to unload. + */ + void + epbf_provider_unload(ebpf_extension_provider_t* provider_context); + + ebpf_error_code_t + ebpf_guid_create(GUID* new_guid); + #ifdef __cplusplus } #endif diff --git a/src/ebpf/include/ebpf_windows.h b/src/ebpf/include/ebpf_windows.h index 49b8fa18f..070f2f7b5 100644 --- a/src/ebpf/include/ebpf_windows.h +++ b/src/ebpf/include/ebpf_windows.h @@ -40,6 +40,7 @@ typedef enum _ebpf_error_code EBPF_ERROR_NOT_SUPPORTED, EBPF_ERROR_DUPLICATE_NAME, EBPF_ERROR_ARITHMETIC_OVERFLOW, + EBPF_ERROR_EXTENSION_FAILED_TO_LOAD, } ebpf_error_code_t; typedef struct _ebpf_map_definition diff --git a/src/ebpf/include/kernel/framework.h b/src/ebpf/include/kernel/framework.h index e26962817..1043bcd42 100644 --- a/src/ebpf/include/kernel/framework.h +++ b/src/ebpf/include/kernel/framework.h @@ -6,6 +6,7 @@ #include #include #include +#include #define uint8_t UINT8 #define uint16_t UINT16 #define uint32_t UINT32 @@ -27,4 +28,4 @@ #define ebpf_list_initialize InitializeListHead #define ebpf_list_is_empty IsListEmpty #define ebpf_list_insert_tail InsertTailList -#define ebpf_list_remove_entry RemoveEntryList \ No newline at end of file +#define ebpf_list_remove_entry RemoveEntryList diff --git a/src/ebpf/include/user/framework.h b/src/ebpf/include/user/framework.h index f726c731c..f1ec8fcf5 100644 --- a/src/ebpf/include/user/framework.h +++ b/src/ebpf/include/user/framework.h @@ -12,9 +12,12 @@ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files +#include #include #include +#pragma comment(lib, "rpcrt4") + #define ebpf_assert(x) assert(x) #if !defined(UNREFERENCED_PARAMETER) diff --git a/src/ebpf/libs/execution_context/ebpf_core.c b/src/ebpf/libs/execution_context/ebpf_core.c index b8516b31a..96dd04c7f 100644 --- a/src/ebpf/libs/execution_context/ebpf_core.c +++ b/src/ebpf/libs/execution_context/ebpf_core.c @@ -11,6 +11,15 @@ #include "ebpf_pinning_table.h" #include "ebpf_program.h" +GUID ebpf_global_helper_function_interface_id = {/* 8d2a1d3f-9ce6-473d-b48e-17aa5c5581fe */ + 0x8d2a1d3f, + 0x9ce6, + 0x473d, + {0xb4, 0x8e, 0x17, 0xaa, 0x5c, 0x55, 0x81, 0xfe}}; + +static ebpf_extension_dispatch_table_t* _ebpf_global_helper_function_dispatch_table = NULL; +static ebpf_extension_provider_t* _ebpf_global_helper_function_provider_context = NULL; + static ebpf_pinning_table_t* _ebpf_core_map_pinning_table = NULL; static ebpf_lock_t _ebpf_core_hook_table_lock = {0}; @@ -35,18 +44,6 @@ static const void* _ebpf_program_helpers[] = { (void*)&_ebpf_core_map_update_element, (void*)&_ebpf_core_map_delete_element}; -size_t -ebpf_core_get_global_helper_count() -{ - return EBPF_COUNT_OF(_ebpf_program_helpers); -} - -const void* -ebpf_core_get_global_helper(size_t helper_id) -{ - return _ebpf_program_helpers[helper_id]; -} - static ebpf_error_code_t _ebpf_core_set_hook_entry(ebpf_program_t* code, ebpf_program_type_t program_type) { @@ -101,13 +98,38 @@ ebpf_core_initiate() ebpf_lock_create(&_ebpf_core_hook_table_lock); + _ebpf_global_helper_function_dispatch_table = ebpf_allocate( + EBPF_OFFSET_OF(ebpf_extension_dispatch_table_t, function) + sizeof(_ebpf_program_helpers), + EBPF_MEMORY_NO_EXECUTE); + if (!_ebpf_global_helper_function_dispatch_table) { + return_value = EBPF_ERROR_OUT_OF_RESOURCES; + goto Done; + } + + _ebpf_global_helper_function_dispatch_table->version = 0; + _ebpf_global_helper_function_dispatch_table->size = + EBPF_OFFSET_OF(ebpf_extension_dispatch_table_t, function) + sizeof(_ebpf_program_helpers); + + memcpy(_ebpf_global_helper_function_dispatch_table->function, _ebpf_program_helpers, sizeof(_ebpf_program_helpers)); + + return_value = ebpf_provider_load( + &_ebpf_global_helper_function_provider_context, + &ebpf_global_helper_function_interface_id, + NULL, + NULL, + _ebpf_global_helper_function_dispatch_table, + NULL, + NULL); + + if (return_value != EBPF_ERROR_SUCCESS) { + goto Done; + } + return_value = ebpf_get_code_integrity_state(&_ebpf_core_code_integrity_state); Done: if (return_value != EBPF_ERROR_SUCCESS) { - ebpf_epoch_terminate(); - ebpf_platform_terminate(); - ebpf_handle_table_terminate(); + ebpf_core_terminate(); } return return_value; } @@ -115,6 +137,12 @@ Done: void ebpf_core_terminate() { + epbf_provider_unload(_ebpf_global_helper_function_provider_context); + _ebpf_global_helper_function_provider_context = NULL; + + ebpf_free(_ebpf_global_helper_function_dispatch_table); + _ebpf_global_helper_function_dispatch_table = NULL; + ebpf_handle_table_terminate(); ebpf_pinning_table_free(_ebpf_core_map_pinning_table); diff --git a/src/ebpf/libs/execution_context/ebpf_hook.c b/src/ebpf/libs/execution_context/ebpf_hook.c index f7151ce3b..1ce112874 100644 --- a/src/ebpf/libs/execution_context/ebpf_hook.c +++ b/src/ebpf/libs/execution_context/ebpf_hook.c @@ -17,17 +17,14 @@ typedef struct _ebpf_hook_instance ebpf_program_entry_point_t program_entry_point; ebpf_attach_type_t attach_type; + ebpf_extension_data_t* client_data; ebpf_extension_client_t* extension_client_context; - uint8_t* hook_properties; - size_t hook_properties_length; - + void* provider_binding_context; + ebpf_extension_data_t* provider_data; ebpf_extension_dispatch_table_t* provider_dispatch_table; } ebpf_hook_instance_t; -// TODO: Get the actual GUID for the hook client. -static const GUID _ebpf_hook_client_id = {0}; - ebpf_error_code_t _ebpf_hook_instance_invoke(const ebpf_hook_instance_t* hook, void* program_context); @@ -43,6 +40,7 @@ _ebpf_hook_free(ebpf_object_t* object) ebpf_hook_instance_t* hook = (ebpf_hook_instance_t*)object; ebpf_hook_instance_detach_program(hook); ebpf_extension_unload(hook->extension_client_context); + ebpf_free(hook->client_data); ebpf_epoch_free(hook); } @@ -64,16 +62,26 @@ ebpf_hook_instance_initialize( ebpf_hook_instance_t* hook, ebpf_attach_type_t attach_type, const uint8_t* context_data, size_t context_data_length) { ebpf_error_code_t return_value; + size_t client_data_length; + + ebpf_safe_size_t_add(sizeof(ebpf_extension_data_t), context_data_length, &client_data_length); + + hook->client_data = ebpf_allocate(client_data_length, EBPF_MEMORY_NO_EXECUTE); + if (!hook->client_data) + return EBPF_ERROR_OUT_OF_RESOURCES; + + hook->client_data->version = 0; + hook->client_data->size = (uint16_t)client_data_length; + memcpy(hook->client_data->data, context_data, context_data_length); return_value = ebpf_extension_load( &(hook->extension_client_context), - &_ebpf_hook_client_id, - context_data, - context_data_length, - (ebpf_extension_dispatch_table_t*)&_ebpf_hook_dispatch_table, &attach_type, - &(hook->hook_properties), - &(hook->hook_properties_length), + hook, + hook->client_data, + (ebpf_extension_dispatch_table_t*)&_ebpf_hook_dispatch_table, + &(hook->provider_binding_context), + &(hook->provider_data), &(hook->provider_dispatch_table)); return return_value; @@ -82,11 +90,11 @@ ebpf_hook_instance_initialize( ebpf_error_code_t ebpf_hook_instance_get_properties(ebpf_hook_instance_t* hook, uint8_t** hook_properties, size_t* hook_properties_length) { - if (!hook->hook_properties) + if (!hook->provider_data) return EBPF_ERROR_INVALID_PARAMETER; - *hook_properties = hook->hook_properties; - *hook_properties_length = hook->hook_properties_length; + *hook_properties = hook->provider_data->data; + *hook_properties_length = hook->provider_data->size; return EBPF_ERROR_SUCCESS; } diff --git a/src/ebpf/libs/execution_context/ebpf_program.c b/src/ebpf/libs/execution_context/ebpf_program.c index 6911d1005..70373784f 100644 --- a/src/ebpf/libs/execution_context/ebpf_program.c +++ b/src/ebpf/libs/execution_context/ebpf_program.c @@ -26,6 +26,11 @@ typedef struct _ebpf_program // EBPF_CODE_EBPF struct ubpf_vm* vm; } code_or_vm; + + ebpf_extension_client_t* global_helper_extension_client; + ebpf_extension_data_t* global_helper_provider_data; + ebpf_extension_dispatch_table_t* global_helper_provider_dispatch_table; + } ebpf_program_t; static void @@ -35,6 +40,10 @@ _ebpf_program_free(ebpf_object_t* object) if (!program) return; + if (program->global_helper_extension_client) { + ebpf_extension_unload(program->global_helper_extension_client); + } + if (program->parameters.code_type == EBPF_CODE_NATIVE) { ebpf_epoch_free(program->code_or_vm.code); } else { @@ -66,6 +75,20 @@ ebpf_program_initialize(ebpf_program_t* program, const ebpf_program_parameters_t ebpf_error_code_t return_value; ebpf_utf8_string_t local_program_name = {NULL, 0}; ebpf_utf8_string_t local_section_name = {NULL, 0}; + void* provider_binding_context; + + return_value = ebpf_extension_load( + &program->global_helper_extension_client, + &ebpf_global_helper_function_interface_id, + NULL, + NULL, + NULL, + &provider_binding_context, + &program->global_helper_provider_data, + &program->global_helper_provider_dispatch_table); + + if (return_value != EBPF_ERROR_SUCCESS) + goto Done; return_value = ebpf_duplicate_utf8_string(&local_program_name, &program_parameters->program_name); if (return_value != EBPF_ERROR_SUCCESS) @@ -127,17 +150,19 @@ Done: } static ebpf_error_code_t -_ebpf_program_register_helpers(struct ubpf_vm* vm) +_ebpf_program_register_helpers(ebpf_program_t* program) { size_t index = 0; - size_t count = ebpf_core_get_global_helper_count(); + size_t count = (program->global_helper_provider_dispatch_table->size - + EBPF_OFFSET_OF(ebpf_extension_dispatch_table_t, function)) / + sizeof(program->global_helper_provider_dispatch_table->function); for (index = 0; index < count; index++) { - const void* helper = ebpf_core_get_global_helper(index); + const void* helper = (void*)program->global_helper_provider_dispatch_table->function[index]; if (helper == NULL) continue; - if (ubpf_register(vm, (unsigned int)index, NULL, (void*)helper) < 0) + if (ubpf_register(program->code_or_vm.vm, (unsigned int)index, NULL, (void*)helper) < 0) return EBPF_ERROR_INVALID_PARAMETER; } return EBPF_ERROR_SUCCESS; @@ -160,7 +185,7 @@ ebpf_program_load_byte_code(ebpf_program_t* program, ebpf_instuction_t* instruct // failing. toggle_bounds_check(program->code_or_vm.vm, false); - return_value = _ebpf_program_register_helpers(program->code_or_vm.vm); + return_value = _ebpf_program_register_helpers(program); if (return_value != EBPF_ERROR_SUCCESS) goto Done; diff --git a/src/ebpf/libs/platform/kernel/ebpf_extension_kernel.c b/src/ebpf/libs/platform/kernel/ebpf_extension_kernel.c new file mode 100644 index 000000000..d621990dc --- /dev/null +++ b/src/ebpf/libs/platform/kernel/ebpf_extension_kernel.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) Microsoft Corporation + * SPDX-License-Identifier: MIT + */ +#include "ebpf_platform.h" + +typedef struct _ebpf_extension_client +{ + NPIID npi_id; + NPI_CLIENT_CHARACTERISTICS client_characteristics; + NPI_MODULEID client_id; + void* client_binding_context; + const ebpf_extension_data_t* client_data; + const ebpf_extension_dispatch_table_t* client_dispatch_table; + NPI_MODULEID provider_id; + void* provider_binding_context; + ebpf_extension_data_t* provider_data; + ebpf_extension_dispatch_table_t* provider_dispatch_table; + HANDLE nmr_client_handle; + bool provider_is_attached; +} ebpf_extension_client_t; + +typedef struct _ebpf_extension_provider +{ + NPIID npi_id; + NPI_PROVIDER_CHARACTERISTICS provider_characteristics; + NPI_MODULEID provider_id; + void* provider_binding_context; + const ebpf_extension_data_t* provider_data; + const ebpf_extension_dispatch_table_t* provider_dispatch_table; + HANDLE nmr_provider_handle; + ebpf_provider_client_attach_callback_t client_attach_callback; + ebpf_provider_client_detach_callback_t client_detach_callback; +} ebpf_extension_provider_t; + +typedef struct _ebpf_extension_provider_binding_context +{ + GUID client_id; + ebpf_provider_client_detach_callback_t client_detach_callback; +} ebpf_extension_provider_binding_context; + +NTSTATUS +_ebpf_extension_client_attach_provider( + HANDLE nmr_binding_handle, void* client_context, const NPI_REGISTRATION_INSTANCE* provider_registration_instance) +{ + NTSTATUS status; + ebpf_extension_client_t* local_client_context = (ebpf_extension_client_t*)client_context; + + // Only permit one provider to attach. + if (local_client_context->provider_data != NULL) { + return STATUS_NOINTERFACE; + } + + // Check that the interface matches. + if (memcmp( + provider_registration_instance->NpiId, + &local_client_context->npi_id, + sizeof(local_client_context->npi_id)) != 0) { + return STATUS_NOINTERFACE; + } + + local_client_context->provider_id = *provider_registration_instance->ModuleId; + local_client_context->provider_data = + (ebpf_extension_data_t*)provider_registration_instance->NpiSpecificCharacteristics; + + status = NmrClientAttachProvider( + nmr_binding_handle, + local_client_context->client_binding_context, + local_client_context->client_dispatch_table, + &local_client_context->provider_binding_context, + &local_client_context->provider_dispatch_table); + + local_client_context->provider_is_attached = NT_SUCCESS(status); + + return status; +} + +NTSTATUS +_ebpf_extension_client_detach_provider(void* client_binding_context) +{ + UNREFERENCED_PARAMETER(client_binding_context); + return STATUS_SUCCESS; +} + +void +_ebpf_extension_client_cleanup_binding_context(void* client_binding_context) +{ + UNREFERENCED_PARAMETER(client_binding_context); +} + +ebpf_error_code_t +ebpf_extension_load( + ebpf_extension_client_t** client_context, + const GUID* interface_id, + void* client_binding_context, + const ebpf_extension_data_t* client_data, + const ebpf_extension_dispatch_table_t* client_dispatch_table, + void** provider_binding_context, + ebpf_extension_data_t** provider_data, + ebpf_extension_dispatch_table_t** provider_dispatch_table) +{ + ebpf_error_code_t return_value; + ebpf_extension_client_t* local_client_context; + NPI_CLIENT_CHARACTERISTICS* client_characteristics; + NPI_REGISTRATION_INSTANCE* client_registration_instance; + NTSTATUS status; + + local_client_context = ebpf_allocate(sizeof(ebpf_extension_client_t), EBPF_MEMORY_NO_EXECUTE); + + if (!local_client_context) { + return_value = EBPF_ERROR_OUT_OF_RESOURCES; + goto Done; + } + + memset(local_client_context, 0, sizeof(ebpf_extension_client_t)); + + local_client_context->client_data = client_data; + local_client_context->npi_id = *interface_id; + local_client_context->client_binding_context = client_binding_context; + local_client_context->client_id.Length = sizeof(local_client_context->client_id); + local_client_context->client_id.Type = MIT_GUID; + local_client_context->client_dispatch_table = client_dispatch_table; + + status = ExUuidCreate(&local_client_context->client_id.Guid); + if (!NT_SUCCESS(status)) { + return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD; + goto Done; + } + + client_characteristics = &(local_client_context->client_characteristics); + client_registration_instance = &(client_characteristics->ClientRegistrationInstance); + + client_characteristics->Version = 0; + client_characteristics->Length = sizeof(*client_characteristics); + client_characteristics->ClientAttachProvider = _ebpf_extension_client_attach_provider; + client_characteristics->ClientDetachProvider = _ebpf_extension_client_detach_provider; + client_characteristics->ClientCleanupBindingContext = _ebpf_extension_client_cleanup_binding_context; + + client_registration_instance->Version = 0; + client_registration_instance->Size = sizeof(*client_registration_instance); + client_registration_instance->NpiId = &local_client_context->npi_id; + client_registration_instance->ModuleId = &local_client_context->client_id; + + client_registration_instance->NpiSpecificCharacteristics = &local_client_context->client_data; + + status = NmrRegisterClient(client_characteristics, local_client_context, &local_client_context->nmr_client_handle); + if (!NT_SUCCESS(status)) { + return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD; + goto Done; + } + + if (!local_client_context->provider_is_attached) { + return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD; + goto Done; + } + + *provider_binding_context = local_client_context->provider_binding_context; + *provider_data = local_client_context->provider_data; + *provider_dispatch_table = local_client_context->provider_dispatch_table; + *client_context = local_client_context; + local_client_context = NULL; + return_value = EBPF_ERROR_SUCCESS; + +Done: + ebpf_free(local_client_context); + return return_value; +} + +void +ebpf_extension_unload(ebpf_extension_client_t* client_context) +{ + NTSTATUS status; + if (client_context) { + status = NmrDeregisterClient(client_context->nmr_client_handle); + if (status == STATUS_PENDING) + NmrWaitForClientDeregisterComplete(client_context->nmr_client_handle); + } + + ebpf_free(client_context); +} + +NTSTATUS +_ebpf_extension_provider_attach_client( + HANDLE nmr_binding_handle, + PVOID provider_context, + const NPI_REGISTRATION_INSTANCE* client_registration_instance, + void* client_binding_context, + const void* client_dispatch, + void** provider_binding_context, + const void** provider_dispatch) +{ + NTSTATUS status; + ebpf_error_code_t return_value; + ebpf_extension_provider_t* local_provider_context = (ebpf_extension_provider_t*)provider_context; + ebpf_extension_provider_binding_context* local_provider_binding_context = NULL; + UNREFERENCED_PARAMETER(nmr_binding_handle); + UNREFERENCED_PARAMETER(client_binding_context); + + // Check that the interface matches. + if (memcmp( + client_registration_instance->NpiId, + &local_provider_context->npi_id, + sizeof(local_provider_context->npi_id)) != 0) { + status = STATUS_NOINTERFACE; + goto Done; + } + + local_provider_binding_context = (ebpf_extension_provider_binding_context*)ebpf_allocate( + sizeof(ebpf_extension_provider_binding_context), EBPF_MEMORY_NO_EXECUTE); + + if (!local_provider_binding_context) { + status = STATUS_NOINTERFACE; + goto Done; + } + + local_provider_binding_context->client_id = client_registration_instance->ModuleId->Guid; + local_provider_binding_context->client_detach_callback = local_provider_context->client_detach_callback; + + if (local_provider_context->client_attach_callback) { + return_value = local_provider_context->client_attach_callback( + client_binding_context, + &local_provider_binding_context->client_id, + (const ebpf_extension_data_t*)client_registration_instance->NpiSpecificCharacteristics, + (const ebpf_extension_dispatch_table_t*)client_dispatch); + + if (return_value != EBPF_ERROR_SUCCESS) { + status = STATUS_NOINTERFACE; + goto Done; + } + } + + *provider_binding_context = local_provider_binding_context; + local_provider_binding_context = NULL; + *provider_dispatch = local_provider_context->provider_dispatch_table; + status = STATUS_SUCCESS; + +Done: + ebpf_free(local_provider_binding_context); + return status; +} + +NTSTATUS +_ebpf_extension_provider_detach_client(void* provider_binding_context) +{ + ebpf_extension_provider_binding_context* local_provider_binding_context = + (ebpf_extension_provider_binding_context*)provider_binding_context; + + if (local_provider_binding_context->client_detach_callback) + local_provider_binding_context->client_detach_callback(&local_provider_binding_context->client_id); + + return STATUS_SUCCESS; +} + +void +_ebpf_extension_provider_cleanup_binding_context(void* provider_binding_context) +{ + ebpf_free(provider_binding_context); +} + +ebpf_error_code_t +ebpf_provider_load( + ebpf_extension_provider_t** provider_context, + const GUID* interface_id, + void* provider_binding_context, + const ebpf_extension_data_t* provider_data, + const ebpf_extension_dispatch_table_t* provider_dispatch_table, + ebpf_provider_client_attach_callback_t client_attach_callback, + ebpf_provider_client_detach_callback_t client_detach_callback) +{ + ebpf_error_code_t return_value; + ebpf_extension_provider_t* local_provider_context; + NPI_PROVIDER_CHARACTERISTICS* provider_characteristics; + NPI_REGISTRATION_INSTANCE* provider_registration_instance; + NTSTATUS status; + + local_provider_context = ebpf_allocate(sizeof(ebpf_extension_provider_t), EBPF_MEMORY_NO_EXECUTE); + + if (!local_provider_context) { + return_value = EBPF_ERROR_OUT_OF_RESOURCES; + goto Done; + } + + memset(local_provider_context, 0, sizeof(ebpf_extension_client_t)); + + local_provider_context->provider_binding_context = provider_binding_context; + local_provider_context->provider_data = provider_data; + local_provider_context->npi_id = *interface_id; + local_provider_context->provider_id.Length = sizeof(local_provider_context->provider_id); + local_provider_context->provider_id.Type = MIT_GUID; + local_provider_context->provider_dispatch_table = provider_dispatch_table; + local_provider_context->client_attach_callback = client_attach_callback; + local_provider_context->client_detach_callback = client_detach_callback; + + status = ExUuidCreate(&local_provider_context->provider_id.Guid); + if (!NT_SUCCESS(status)) { + return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD; + goto Done; + } + + provider_characteristics = &(local_provider_context->provider_characteristics); + provider_registration_instance = &(provider_characteristics->ProviderRegistrationInstance); + + provider_characteristics->Version = 0; + provider_characteristics->Length = sizeof(*provider_characteristics); + provider_characteristics->ProviderAttachClient = _ebpf_extension_provider_attach_client; + provider_characteristics->ProviderDetachClient = _ebpf_extension_provider_detach_client; + provider_characteristics->ProviderCleanupBindingContext = _ebpf_extension_provider_cleanup_binding_context; + + provider_registration_instance->Version = 0; + provider_registration_instance->Size = sizeof(*provider_registration_instance); + provider_registration_instance->NpiId = &local_provider_context->npi_id; + provider_registration_instance->ModuleId = &local_provider_context->provider_id; + + provider_registration_instance->NpiSpecificCharacteristics = &local_provider_context->provider_data; + + status = NmrRegisterProvider( + provider_characteristics, local_provider_context, &local_provider_context->nmr_provider_handle); + if (!NT_SUCCESS(status)) { + return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD; + goto Done; + } + + *provider_context = local_provider_context; + local_provider_context = NULL; + return_value = EBPF_ERROR_SUCCESS; + +Done: + ebpf_free(local_provider_context); + return return_value; +} + +void +epbf_provider_unload(ebpf_extension_provider_t* provider_context) +{ + NTSTATUS status; + if (provider_context) { + status = NmrDeregisterProvider(provider_context->nmr_provider_handle); + if (status == STATUS_PENDING) + NmrWaitForProviderDeregisterComplete(provider_context->nmr_provider_handle); + } + + ebpf_free(provider_context); +} diff --git a/src/ebpf/libs/platform/kernel/platform_kernel.c b/src/ebpf/libs/platform/kernel/ebpf_platform_kernel.c similarity index 100% rename from src/ebpf/libs/platform/kernel/platform_kernel.c rename to src/ebpf/libs/platform/kernel/ebpf_platform_kernel.c diff --git a/src/ebpf/libs/platform/kernel/platform_kernel.vcxproj b/src/ebpf/libs/platform/kernel/platform_kernel.vcxproj index 9f5d49b73..9963d3a32 100644 --- a/src/ebpf/libs/platform/kernel/platform_kernel.vcxproj +++ b/src/ebpf/libs/platform/kernel/platform_kernel.vcxproj @@ -43,7 +43,8 @@ - + + diff --git a/src/ebpf/libs/platform/kernel/platform_kernel.vcxproj.filters b/src/ebpf/libs/platform/kernel/platform_kernel.vcxproj.filters index fe0846e3f..e64cfe2b1 100644 --- a/src/ebpf/libs/platform/kernel/platform_kernel.vcxproj.filters +++ b/src/ebpf/libs/platform/kernel/platform_kernel.vcxproj.filters @@ -19,7 +19,7 @@ - + Source Files @@ -34,6 +34,9 @@ Source Files + + Source Files + diff --git a/src/ebpf/libs/platform/user/ebpf_extension_user.c b/src/ebpf/libs/platform/user/ebpf_extension_user.c new file mode 100644 index 000000000..6104f5bea --- /dev/null +++ b/src/ebpf/libs/platform/user/ebpf_extension_user.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) Microsoft Corporation + * SPDX-License-Identifier: MIT + */ +#include "ebpf_platform.h" + +typedef struct _ebpf_extension_client +{ + GUID client_id; + GUID interface_id; + void* client_binding_context; + const ebpf_extension_data_t* client_data; + const ebpf_extension_dispatch_table_t* client_dispatch_table; +} ebpf_extension_client_t; + +typedef struct _ebpf_extension_provider +{ + GUID interface_id; + void* provider_binding_context; + const ebpf_extension_data_t* provider_data; + const ebpf_extension_dispatch_table_t* provider_dispatch_table; + ebpf_provider_client_attach_callback_t client_attach_callback; + ebpf_provider_client_detach_callback_t client_detach_callback; + ebpf_hash_table_t* client_table; +} ebpf_extension_provider_t; + +ebpf_lock_t _ebpf_provider_table_lock = {0}; +ebpf_hash_table_t* _ebpf_provider_table = NULL; + +ebpf_error_code_t +ebpf_extension_load( + ebpf_extension_client_t** client_context, + const GUID* interface_id, + void* client_binding_context, + const ebpf_extension_data_t* client_data, + const ebpf_extension_dispatch_table_t* client_dispatch_table, + void** provider_binding_context, + ebpf_extension_data_t** provider_data, + ebpf_extension_dispatch_table_t** provider_dispatch_table) +{ + ebpf_error_code_t return_value; + ebpf_lock_state_t state; + ebpf_extension_provider_t* local_extension_provider = NULL; + ebpf_extension_provider_t** hash_table_find_result = NULL; + ebpf_extension_client_t* local_extension_client = NULL; + ebpf_lock_lock(&_ebpf_provider_table_lock, &state); + + if (!_ebpf_provider_table) { + return_value = EBPF_ERROR_NOT_FOUND; + goto Done; + } + + local_extension_client = ebpf_allocate(sizeof(ebpf_extension_client_t), EBPF_MEMORY_NO_EXECUTE); + if (!local_extension_client) { + return_value = EBPF_ERROR_OUT_OF_RESOURCES; + goto Done; + } + + memset(local_extension_client, 0, sizeof(ebpf_extension_client_t)); + local_extension_client->client_binding_context = client_binding_context; + local_extension_client->client_data = client_data; + local_extension_client->client_dispatch_table = client_dispatch_table; + local_extension_client->interface_id = *interface_id; + + ebpf_guid_create(&local_extension_client->client_id); + + return_value = + ebpf_hash_table_find(_ebpf_provider_table, (const uint8_t*)interface_id, (uint8_t**)&hash_table_find_result); + if (return_value != EBPF_ERROR_SUCCESS) { + return_value = EBPF_ERROR_NOT_FOUND; + goto Done; + } + local_extension_provider = *hash_table_find_result; + + return_value = ebpf_hash_table_update( + local_extension_provider->client_table, + (const uint8_t*)&local_extension_client->client_id, + (const uint8_t*)&local_extension_client); + if (return_value != EBPF_ERROR_SUCCESS) { + goto Done; + } + + if (local_extension_provider->client_attach_callback) { + return_value = local_extension_provider->client_attach_callback( + local_extension_client->client_binding_context, + &local_extension_client->client_id, + client_data, + client_dispatch_table); + if (return_value != EBPF_ERROR_SUCCESS) { + return_value = EBPF_ERROR_NOT_FOUND; + goto Done; + } + } + *client_context = local_extension_client; + local_extension_client = NULL; + + *provider_binding_context = local_extension_provider->provider_binding_context; + *provider_data = local_extension_provider->provider_data; + *provider_dispatch_table = local_extension_provider->provider_dispatch_table; + +Done: + if (local_extension_provider && local_extension_client) { + ebpf_hash_table_delete( + local_extension_provider->client_table, (const uint8_t*)&local_extension_client->client_id); + } + + ebpf_lock_unlock(&_ebpf_provider_table_lock, &state); + return return_value; +} + +void +ebpf_extension_unload(ebpf_extension_client_t* client_context) +{ + ebpf_error_code_t return_value; + ebpf_lock_state_t state; + ebpf_extension_provider_t** hash_table_find_result = NULL; + ebpf_extension_provider_t* local_extension_provider = NULL; + + if (client_context) + return; + + ebpf_lock_lock(&_ebpf_provider_table_lock, &state); + + if (!_ebpf_provider_table) { + goto Done; + } + + return_value = ebpf_hash_table_find( + _ebpf_provider_table, (const uint8_t*)&client_context->interface_id, (uint8_t**)&hash_table_find_result); + if (return_value != EBPF_ERROR_SUCCESS) { + goto Done; + } + local_extension_provider = *hash_table_find_result; + + if (local_extension_provider->client_detach_callback) { + local_extension_provider->client_detach_callback(&client_context->client_id); + } + ebpf_hash_table_delete(local_extension_provider->client_table, (const uint8_t*)&client_context->client_id); + +Done: + ebpf_free(client_context); + ebpf_lock_unlock(&_ebpf_provider_table_lock, &state); +} + +ebpf_error_code_t +ebpf_provider_load( + ebpf_extension_provider_t** provider_context, + const GUID* interface_id, + void* provider_binding_context, + const ebpf_extension_data_t* provider_data, + const ebpf_extension_dispatch_table_t* provider_dispatch_table, + ebpf_provider_client_attach_callback_t client_attach_callback, + ebpf_provider_client_detach_callback_t client_detach_callback) +{ + ebpf_error_code_t return_value; + ebpf_lock_state_t state; + ebpf_extension_provider_t* local_extension_provider = NULL; + ebpf_lock_lock(&_ebpf_provider_table_lock, &state); + + if (!_ebpf_provider_table) { + return_value = + ebpf_hash_table_create(&_ebpf_provider_table, ebpf_allocate, ebpf_free, sizeof(GUID), sizeof(void*), NULL); + if (return_value != EBPF_ERROR_SUCCESS) + goto Done; + } + + return_value = + ebpf_hash_table_find(_ebpf_provider_table, (const uint8_t*)interface_id, (uint8_t**)&local_extension_provider); + if (return_value == EBPF_ERROR_SUCCESS) { + return_value = EBPF_ERROR_DUPLICATE_NAME; + local_extension_provider = NULL; + goto Done; + } + + local_extension_provider = ebpf_allocate(sizeof(ebpf_extension_provider_t), EBPF_MEMORY_NO_EXECUTE); + if (!local_extension_provider) { + return_value = EBPF_ERROR_OUT_OF_RESOURCES; + goto Done; + } + memset(local_extension_provider, 0, sizeof(ebpf_extension_provider_t)); + + local_extension_provider->interface_id = *interface_id; + local_extension_provider->provider_binding_context = provider_binding_context; + local_extension_provider->provider_data = provider_data; + local_extension_provider->provider_dispatch_table = provider_dispatch_table; + local_extension_provider->client_attach_callback = client_attach_callback; + local_extension_provider->client_detach_callback = client_detach_callback; + + return_value = ebpf_hash_table_create( + &local_extension_provider->client_table, ebpf_allocate, ebpf_free, sizeof(GUID), sizeof(void*), NULL); + if (return_value != EBPF_ERROR_SUCCESS) { + goto Done; + } + + return_value = ebpf_hash_table_update( + _ebpf_provider_table, (const uint8_t*)interface_id, (const uint8_t*)&local_extension_provider); + if (return_value != EBPF_ERROR_SUCCESS) + goto Done; + + *provider_context = local_extension_provider; + local_extension_provider = NULL; + +Done: + ebpf_lock_unlock(&_ebpf_provider_table_lock, &state); + ebpf_free(local_extension_provider); + return return_value; +} + +void +epbf_provider_unload(ebpf_extension_provider_t* provider_context) +{ + ebpf_error_code_t return_value; + ebpf_lock_state_t state; + ebpf_extension_provider_t* local_extension_provider = NULL; + GUID next_key; + + if (!provider_context) + return; + + ebpf_lock_lock(&_ebpf_provider_table_lock, &state); + + if (!_ebpf_provider_table) { + goto Done; + } + + ebpf_hash_table_delete(_ebpf_provider_table, (const uint8_t*)&provider_context->interface_id); + + return_value = ebpf_hash_table_next_key(_ebpf_provider_table, NULL, (uint8_t*)&next_key); + if (return_value == EBPF_ERROR_NO_MORE_KEYS) { + ebpf_hash_table_destroy(_ebpf_provider_table); + _ebpf_provider_table = NULL; + } + +Done: + ebpf_lock_unlock(&_ebpf_provider_table_lock, &state); + ebpf_free(local_extension_provider); +} diff --git a/src/ebpf/libs/platform/user/platform_user.cpp b/src/ebpf/libs/platform/user/ebpf_platform_user.cpp similarity index 98% rename from src/ebpf/libs/platform/user/platform_user.cpp rename to src/ebpf/libs/platform/user/ebpf_platform_user.cpp index d7c0849cf..939c23af1 100644 --- a/src/ebpf/libs/platform/user/platform_user.cpp +++ b/src/ebpf/libs/platform/user/ebpf_platform_user.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -327,3 +328,10 @@ ebpf_free_timer_work_item(ebpf_timer_work_item_t* work_item) CloseThreadpoolTimer(work_item->threadpool_timer); ebpf_free(work_item); } + +ebpf_error_code_t +ebpf_guid_create(GUID* new_guid) +{ + UuidCreate(new_guid); + return EBPF_ERROR_SUCCESS; +} \ No newline at end of file diff --git a/src/ebpf/libs/platform/user/platform_user.vcxproj b/src/ebpf/libs/platform/user/platform_user.vcxproj index 4c54589b6..aace8684e 100644 --- a/src/ebpf/libs/platform/user/platform_user.vcxproj +++ b/src/ebpf/libs/platform/user/platform_user.vcxproj @@ -31,7 +31,8 @@ - + + 16.0 diff --git a/src/ebpf/libs/platform/user/platform_user.vcxproj.filters b/src/ebpf/libs/platform/user/platform_user.vcxproj.filters index 7a3cf28a1..dcdc1e0ac 100644 --- a/src/ebpf/libs/platform/user/platform_user.vcxproj.filters +++ b/src/ebpf/libs/platform/user/platform_user.vcxproj.filters @@ -27,7 +27,7 @@ - + Source Files @@ -45,5 +45,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/src/ebpf/sys/EbpfCore.vcxproj b/src/ebpf/sys/EbpfCore.vcxproj index 836206cb7..bd05c0944 100644 --- a/src/ebpf/sys/EbpfCore.vcxproj +++ b/src/ebpf/sys/EbpfCore.vcxproj @@ -97,7 +97,7 @@ %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;POOL_NX_OPTIN_AUTO - %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib;$(DDK_LIB_PATH)\netio.lib; @@ -117,7 +117,7 @@ %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;POOL_NX_OPTIN_AUTO - %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib;execution_context_kernel.lib;ubpf_kernel.lib + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib;$(DDK_LIB_PATH)\netio.lib;execution_context_kernel.lib;ubpf_kernel.lib $(OutDir) diff --git a/src/test/end_to_end/end_to_end.cpp b/src/test/end_to_end/end_to_end.cpp index 8bf391967..35b14ee54 100644 --- a/src/test/end_to_end/end_to_end.cpp +++ b/src/test/end_to_end/end_to_end.cpp @@ -740,4 +740,67 @@ TEST_CASE("epoch_test_two_threads", "[epoch_test_two_threads]") std::thread thread_2(epoch); thread_1.join(); thread_2.join(); +} + +TEST_CASE("extension_test", "[extension_test]") +{ + auto client_function = []() { return EBPF_ERROR_SUCCESS; }; + auto provider_function = []() { return EBPF_ERROR_SUCCESS; }; + auto provider_attach = [](const GUID* client_id, + void* client_binding_context, + const ebpf_extension_data_t* client_data, + const ebpf_extension_dispatch_table_t* client_dispatch_table) { + UNREFERENCED_PARAMETER(client_id); + UNREFERENCED_PARAMETER(client_data); + UNREFERENCED_PARAMETER(client_dispatch_table); + UNREFERENCED_PARAMETER(client_binding_context); + return EBPF_ERROR_SUCCESS; + }; + auto provider_detach = [](const GUID* client_id) { + UNREFERENCED_PARAMETER(client_id); + return EBPF_ERROR_SUCCESS; + }; + ebpf_extension_dispatch_table_t client_dispatch_table = { + 0, sizeof(ebpf_extension_dispatch_table_t), client_function}; + ebpf_extension_dispatch_table_t provider_dispatch_table = { + 0, sizeof(ebpf_extension_dispatch_table_t), provider_function}; + ebpf_extension_data_t client_data; + ebpf_extension_data_t provider_data; + GUID interface_id; + + ebpf_extension_dispatch_table_t* returned_provider_dispatch_table; + ebpf_extension_data_t* returned_provider_data; + + ebpf_extension_provider_t* provider_context; + ebpf_extension_client_t* client_context; + void* provider_binding_context; + + ebpf_guid_create(&interface_id); + + REQUIRE( + ebpf_provider_load( + &provider_context, + &interface_id, + nullptr, + &provider_data, + &provider_dispatch_table, + provider_attach, + provider_detach) == EBPF_ERROR_SUCCESS); + + REQUIRE( + ebpf_extension_load( + &client_context, + &interface_id, + nullptr, + &client_data, + &client_dispatch_table, + &provider_binding_context, + &returned_provider_data, + &returned_provider_dispatch_table) == EBPF_ERROR_SUCCESS); + + REQUIRE(returned_provider_data == &provider_data); + REQUIRE(returned_provider_dispatch_table == &provider_dispatch_table); + + ebpf_extension_unload(client_context); + epbf_provider_unload(provider_context); } \ No newline at end of file