From 4b53d101aa300c26b3bb5f0571585e9381d97956 Mon Sep 17 00:00:00 2001 From: Alan Jowett Date: Wed, 26 May 2021 14:37:04 -0600 Subject: [PATCH] Add support for allocating pages for code that can be marked read/execute (#228) * Add LLVM for code generation proposal Signed-off-by: Alan Jowett * Revert "Add LLVM for code generation proposal" This reverts commit cd896afd945fd4d98dffe6763fbd0a5b8e6981f5. * Work in progress Signed-off-by: Alan Jowett * Add doxygen Signed-off-by: Alan Jowett * PR feedback Signed-off-by: Alan Jowett * PR feedback Signed-off-by: Alan Jowett * PR feedback Signed-off-by: Alan Jowett --- ebpfcore/EbpfCore.vcxproj | 2 +- libs/platform/ebpf_platform.h | 53 +++++++++++++++++ libs/platform/kernel/ebpf_platform_kernel.c | 64 ++++++++++++++++++++ libs/platform/unit/platform_unit_test.cpp | 35 +++++++---- libs/platform/user/ebpf_platform_user.cpp | 66 +++++++++++++++++++++ netebpfext/netebpfext.vcxproj | 2 +- 6 files changed, 210 insertions(+), 12 deletions(-) diff --git a/ebpfcore/EbpfCore.vcxproj b/ebpfcore/EbpfCore.vcxproj index c4396dbce..7bd8b9ac5 100644 --- a/ebpfcore/EbpfCore.vcxproj +++ b/ebpfcore/EbpfCore.vcxproj @@ -65,7 +65,7 @@ Driver KMDF Universal - false + Spectre diff --git a/libs/platform/ebpf_platform.h b/libs/platform/ebpf_platform.h index b899f9333..aa9338e6d 100644 --- a/libs/platform/ebpf_platform.h +++ b/libs/platform/ebpf_platform.h @@ -124,6 +124,59 @@ extern "C" void ebpf_free(void* memory); + typedef enum _ebpf_page_protection + { + EBPF_PAGE_PROTECT_READ_ONLY, + EBPF_PAGE_PROTECT_READ_WRITE, + EBPF_PAGE_PROTECT_READ_EXECUTE, + } ebpf_page_protection_t; + + typedef struct _ebpf_memory_descriptor ebpf_memory_descriptor_t; + + /** + * @brief Allocate pages from physical memory and create a mapping into the + * system address space. + * + * @param[in] length Size of memory to allocate (internally this gets rounded + * up to a page boundary). + * @return Pointer to an ebpf_memory_descriptor_t on success, NULL on failure. + */ + ebpf_memory_descriptor_t* + ebpf_map_memory(size_t length); + + /** + * @brief Release physical memory previously allocated via ebpf_map_memory. + * + * @param[in] memory_descriptor Pointer to ebpf_memory_descriptor_t describing + * allocated pages. + */ + void + ebpf_unmap_memory(ebpf_memory_descriptor_t* memory_descriptor); + + /** + * @brief Change the page protection on memory allocated via + * ebpf_map_memory. + * + * @param[in] memory_descriptor Pointer to an ebpf_memory_descriptor_t + * describing allocated pages. + * @param[in] protection The new page protection to apply. + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_INVALID_ARGUMENT An invalid argument was supplied. + */ + ebpf_result_t + ebpf_protect_memory(const ebpf_memory_descriptor_t* memory_descriptor, ebpf_page_protection_t protection); + + /** + * @brief Given an ebpf_memory_descriptor_t allocated via ebpf_map_memory + * obtain the base virtual address. + * + * @param[in] memory_descriptor Pointer to an ebpf_memory_descriptor_t + * describing allocated pages. + * @return Base virtual address of pages that have been allocated. + */ + void* + ebpf_memory_descriptor_get_base_address(ebpf_memory_descriptor_t* memory_descriptor); + /** * @brief Allocate and copy a UTF-8 string. * diff --git a/libs/platform/kernel/ebpf_platform_kernel.c b/libs/platform/kernel/ebpf_platform_kernel.c index 03840310c..bc073f6c8 100644 --- a/libs/platform/kernel/ebpf_platform_kernel.c +++ b/libs/platform/kernel/ebpf_platform_kernel.c @@ -10,6 +10,11 @@ #include +typedef struct _ebpf_memory_descriptor +{ + MDL memory_descriptor_list; +} ebpf_memory_descriptor_t; + typedef enum _ebpf_pool_tag { EBPF_POOL_TAG = 'fpbe' @@ -39,6 +44,65 @@ ebpf_free(void* memory) ExFreePool(memory); } +ebpf_memory_descriptor_t* +ebpf_map_memory(size_t length) +{ + MDL* memory_descriptor_list = NULL; + PHYSICAL_ADDRESS start_address; + PHYSICAL_ADDRESS end_address; + PHYSICAL_ADDRESS page_size; + start_address.QuadPart = 0; + end_address.QuadPart = -1; + page_size.QuadPart = PAGE_SIZE; + memory_descriptor_list = + MmAllocatePagesForMdlEx(start_address, end_address, page_size, length, MmCached, MM_ALLOCATE_FULLY_REQUIRED); + + if (memory_descriptor_list) { + MmProbeAndLockPages(memory_descriptor_list, KernelMode, IoWriteAccess); + } + return (ebpf_memory_descriptor_t*)memory_descriptor_list; +} + +void +ebpf_unmap_memory(ebpf_memory_descriptor_t* memory_descriptor) +{ + MmUnlockPages(&memory_descriptor->memory_descriptor_list); + MmFreePagesFromMdl(&memory_descriptor->memory_descriptor_list); + ExFreePool(memory_descriptor); +} + +ebpf_result_t +ebpf_protect_memory(const ebpf_memory_descriptor_t* memory_descriptor, ebpf_page_protection_t protection) +{ + NTSTATUS status; + ULONG mm_protection_state = 0; + switch (protection) { + case EBPF_PAGE_PROTECT_READ_ONLY: + mm_protection_state = PAGE_READONLY; + break; + case EBPF_PAGE_PROTECT_READ_WRITE: + mm_protection_state = PAGE_READWRITE; + break; + case EBPF_PAGE_PROTECT_READ_EXECUTE: + mm_protection_state = PAGE_EXECUTE_READ; + break; + default: + return EBPF_INVALID_ARGUMENT; + } + + status = MmProtectMdlSystemAddress((MDL*)&memory_descriptor->memory_descriptor_list, mm_protection_state); + if (!NT_SUCCESS(status)) + return EBPF_INVALID_ARGUMENT; + + return EBPF_SUCCESS; +} + +void* +ebpf_memory_descriptor_get_base_address(ebpf_memory_descriptor_t* memory_descriptor) +{ + return MmGetSystemAddressForMdlSafe(&memory_descriptor->memory_descriptor_list, NormalPagePriority); +} + // There isn't an official API to query this information from kernel. // Use NtQuerySystemInformation with struct + header from winternl.h. diff --git a/libs/platform/unit/platform_unit_test.cpp b/libs/platform/unit/platform_unit_test.cpp index 5e7edee29..d8aa03646 100644 --- a/libs/platform/unit/platform_unit_test.cpp +++ b/libs/platform/unit/platform_unit_test.cpp @@ -59,14 +59,14 @@ TEST_CASE("pinning_test", "[pinning_test]") some_object_t an_object; some_object_t another_object; - some_object_t* some_object; + some_object_t* some_object = nullptr; ebpf_utf8_string_t foo = EBPF_UTF8_STRING_FROM_CONST_STRING("foo"); ebpf_utf8_string_t bar = EBPF_UTF8_STRING_FROM_CONST_STRING("bar"); ebpf_object_initialize(&an_object.object, EBPF_OBJECT_MAP, [](ebpf_object_t*) {}); ebpf_object_initialize(&another_object.object, EBPF_OBJECT_MAP, [](ebpf_object_t*) {}); - ebpf_pinning_table_t* pinning_table; + ebpf_pinning_table_t* pinning_table = nullptr; REQUIRE(ebpf_pinning_table_allocate(&pinning_table) == EBPF_SUCCESS); REQUIRE(ebpf_pinning_table_insert(pinning_table, &foo, &an_object.object) == EBPF_SUCCESS); @@ -153,9 +153,9 @@ TEST_CASE("extension_test", "[extension_test]") const ebpf_extension_dispatch_table_t* returned_provider_dispatch_table; const ebpf_extension_data_t* returned_provider_data; - ebpf_extension_provider_t* provider_context; - ebpf_extension_client_t* client_context; - void* provider_binding_context; + ebpf_extension_provider_t* provider_context = nullptr; + ebpf_extension_client_t* client_context = nullptr; + void* provider_binding_context = nullptr; ebpf_guid_create(&interface_id); @@ -237,14 +237,15 @@ TEST_CASE("program_type_info", "[program_type_info]") EBPF_RETURN_TYPE_PTR_TO_MAP_VALUE_OR_NULL, {EBPF_ARGUMENT_TYPE_PTR_TO_MAP, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_KEY}}, }; - ebpf_context_descriptor_t context_descriptor{sizeof(xdp_md_t), - EBPF_OFFSET_OF(xdp_md_t, data), - EBPF_OFFSET_OF(xdp_md_t, data_end), - EBPF_OFFSET_OF(xdp_md_t, data_meta)}; + ebpf_context_descriptor_t context_descriptor{ + sizeof(xdp_md_t), + EBPF_OFFSET_OF(xdp_md_t, data), + EBPF_OFFSET_OF(xdp_md_t, data_end), + EBPF_OFFSET_OF(xdp_md_t, data_meta)}; ebpf_program_type_descriptor_t program_type{"xdp", &context_descriptor}; ebpf_program_information_t program_information{program_type, _countof(helper_functions), helper_functions}; ebpf_program_information_t* new_program_information = nullptr; - uint8_t* buffer; + uint8_t* buffer = nullptr; unsigned long buffer_size; REQUIRE(ebpf_program_information_encode(&program_information, &buffer, &buffer_size) == EBPF_SUCCESS); REQUIRE(ebpf_program_information_decode(&new_program_information, buffer, buffer_size) == EBPF_SUCCESS); @@ -298,3 +299,17 @@ TEST_CASE("access_check", "[access_check]") REQUIRE((result = ebpf_access_check(sd, 1, &generic_mapping), LocalFree(sd), result == EBPF_ERROR_ACCESS_DENIED)); } + +TEST_CASE("memory_map_test", "[memory_map_test]") +{ + ebpf_result_t result; + ebpf_memory_descriptor_t* memory_descriptor = nullptr; + REQUIRE((memory_descriptor = ebpf_map_memory(100)) != nullptr); + REQUIRE( + (result = ebpf_protect_memory(memory_descriptor, EBPF_PAGE_PROTECT_READ_WRITE), + result != EBPF_SUCCESS ? ebpf_unmap_memory(memory_descriptor) : (void)0, + result == EBPF_SUCCESS)); + memset(ebpf_memory_descriptor_get_base_address(memory_descriptor), 0xCC, 100); + REQUIRE(ebpf_protect_memory(memory_descriptor, EBPF_PAGE_PROTECT_READ_ONLY) == EBPF_SUCCESS); + ebpf_unmap_memory(memory_descriptor); +} \ No newline at end of file diff --git a/libs/platform/user/ebpf_platform_user.cpp b/libs/platform/user/ebpf_platform_user.cpp index 4ba8a1d5d..69a3ea07f 100644 --- a/libs/platform/user/ebpf_platform_user.cpp +++ b/libs/platform/user/ebpf_platform_user.cpp @@ -131,6 +131,72 @@ ebpf_free(void* memory) free(memory); } } +struct _ebpf_memory_descriptor +{ + void* base; + size_t length; +}; +typedef struct _ebpf_memory_descriptor ebpf_memory_descriptor_t; + +ebpf_memory_descriptor_t* +ebpf_map_memory(size_t length) +{ + ebpf_memory_descriptor_t* descriptor = (ebpf_memory_descriptor_t*)malloc(sizeof(ebpf_memory_descriptor_t)); + if (!descriptor) { + return nullptr; + } + + descriptor->base = VirtualAlloc(0, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + descriptor->length = length; + + if (!descriptor->base) { + free(descriptor); + descriptor = nullptr; + } + return descriptor; +} + +void +ebpf_unmap_memory(ebpf_memory_descriptor_t* memory_descriptor) +{ + if (memory_descriptor) { + VirtualFree(memory_descriptor->base, 0, MEM_RELEASE); + free(memory_descriptor); + } +} + +ebpf_result_t +ebpf_protect_memory(const ebpf_memory_descriptor_t* memory_descriptor, ebpf_page_protection_t protection) +{ + ULONG mm_protection_state = 0; + ULONG old_mm_protection_state = 0; + switch (protection) { + case EBPF_PAGE_PROTECT_READ_ONLY: + mm_protection_state = PAGE_READONLY; + break; + case EBPF_PAGE_PROTECT_READ_WRITE: + mm_protection_state = PAGE_READWRITE; + break; + case EBPF_PAGE_PROTECT_READ_EXECUTE: + mm_protection_state = PAGE_EXECUTE_READ; + break; + default: + return EBPF_INVALID_ARGUMENT; + } + + if (!VirtualProtect( + memory_descriptor->base, memory_descriptor->length, mm_protection_state, &old_mm_protection_state)) { + return EBPF_INVALID_ARGUMENT; + } + + return EBPF_SUCCESS; +} + +void* +ebpf_memory_descriptor_get_base_address(ebpf_memory_descriptor_t* memory_descriptor) +{ + return memory_descriptor->base; +} ebpf_result_t ebpf_safe_size_t_multiply(size_t multiplicand, size_t multiplier, size_t* result) diff --git a/netebpfext/netebpfext.vcxproj b/netebpfext/netebpfext.vcxproj index b591a7ab8..3686259f2 100644 --- a/netebpfext/netebpfext.vcxproj +++ b/netebpfext/netebpfext.vcxproj @@ -65,7 +65,7 @@ Driver KMDF Universal - false + Spectre