From a6c687db0b96985d278835f8156e18227ada8185 Mon Sep 17 00:00:00 2001 From: Alan Jowett Date: Thu, 20 Jun 2024 12:45:54 -0700 Subject: [PATCH] Add support for program loading flags. Signed-off-by: Alan Jowett --- docs/eBpfExtensions.md | 6 +++ ebpfapi/Source.def | 2 + include/bpf/libbpf.h | 25 ++++++++++ include/ebpf_extension.h | 2 + include/ebpf_windows.h | 8 ++++ libs/api/api_internal.h | 13 ++++++ libs/api/ebpf_api.cpp | 30 ++++++++++++ libs/api/libbpf_program.cpp | 16 +++++++ libs/execution_context/ebpf_core.c | 25 ++++++++++ libs/execution_context/ebpf_link.c | 14 ++++-- libs/execution_context/ebpf_program.c | 15 +++++- libs/execution_context/ebpf_program.h | 18 +++++++ libs/execution_context/ebpf_protocol.h | 8 ++++ libs/shared/shared_common.c | 5 +- tests/end_to_end/helpers.h | 9 ++++ tests/unit/libbpf_test.cpp | 65 ++++++++++++++++++++++++++ 16 files changed, 252 insertions(+), 9 deletions(-) diff --git a/docs/eBpfExtensions.md b/docs/eBpfExtensions.md index 175b856aa0..77395ffbe0 100644 --- a/docs/eBpfExtensions.md +++ b/docs/eBpfExtensions.md @@ -393,6 +393,12 @@ ebpf_extension_data_t* extension_data = (ebpf_extension_data_t*)ClientRegistrati attach_parameter = extension_data->data; ``` +### `ebpf_extension_data_t` Struct +This structure contains the additional data passed from the application to the attach provider. It contains the following fields: +* `data` Attach type specific data. See documentation for the attach type provider for the format of this data. +* `data_size` The length of the attach type specific data. +* `prog_attach_flags` A collection of attach type specific flags passed from the application to the attach provider. + The per-client data structure should be returned as the `ProviderBindingContext` output parameter. Upon diff --git a/ebpfapi/Source.def b/ebpfapi/Source.def index 9622709967..9d0725fe8a 100644 --- a/ebpfapi/Source.def +++ b/ebpfapi/Source.def @@ -79,6 +79,7 @@ EXPORTS bpf_program__attach_xdp bpf_program__autoload bpf_program__fd + bpf_program__flags bpf_program__get_expected_attach_type bpf_program__get_type=bpf_program__type bpf_program__insn_cnt @@ -90,6 +91,7 @@ EXPORTS bpf_program__section_name bpf_program__set_autoload bpf_program__set_expected_attach_type + bpf_program__set_flags bpf_program__set_type bpf_program__type bpf_program__unload diff --git a/include/bpf/libbpf.h b/include/bpf/libbpf.h index 90890060f7..37fbd5eff8 100644 --- a/include/bpf/libbpf.h +++ b/include/bpf/libbpf.h @@ -917,6 +917,31 @@ void ring_buffer__free(struct ring_buffer* rb); /** @} */ +/** + * @brief Query the BPF program flags. + * + * @param[in] prog A pointer to the BPF program. + * @return The flags of the BPF program. + */ +__u32 +bpf_program__flags(const struct bpf_program* prog); + +/** + * @brief Set the BPF program flags. + * The set of flags is defined by the program type. Neither libbpf nor the eBPF runtime check the flags and only + * the extension that handles this program type will interpret them. See the documentation for the + * program type for what flags are defined for that program type. + * + * @param[in] prog A pointer to the BPF program. + * @param[in] flags The flags to set. + * + * @retval 0 The operation was successful. + * @retval <0 An error occurred, and errno was set. + * + */ +int +bpf_program__set_flags(struct bpf_program* prog, __u32 flags); + #else #pragma warning(push) #pragma warning(disable : 4200) // Zero-sized array in struct/union diff --git a/include/ebpf_extension.h b/include/ebpf_extension.h index 509f6f85a8..f6faf13da7 100644 --- a/include/ebpf_extension.h +++ b/include/ebpf_extension.h @@ -95,6 +95,8 @@ typedef struct _ebpf_extension_data { ebpf_extension_header_t header; const void* data; + size_t data_size; + uint64_t prog_attach_flags; } ebpf_extension_data_t; typedef struct _ebpf_attach_provider_data diff --git a/include/ebpf_windows.h b/include/ebpf_windows.h index 8ab11595e2..dda56f2995 100644 --- a/include/ebpf_windows.h +++ b/include/ebpf_windows.h @@ -73,6 +73,14 @@ typedef enum _ebpf_helper_function #define EBPF_ATTACH_CLIENT_DATA_CURRENT_VERSION 1 #define EBPF_PROGRAM_INFORMATION_CLIENT_DATA_CURRENT_VERSION 1 +#define EBPF_ATTACH_CLIENT_DATA_VERSION_SIZE EBPF_SIZE_INCLUDING_FIELD(ebpf_extension_data_t, prog_attach_flags) +#define EBPF_ATTACH_CLIENT_DATA_VERSION_TOTAL_SIZE sizeof(ebpf_extension_data_t) +#define EBPF_ATTACH_CLIENT_DATA_HEADER_VERSION \ + { \ + EBPF_ATTACH_CLIENT_DATA_CURRENT_VERSION, EBPF_ATTACH_CLIENT_DATA_VERSION_SIZE, \ + EBPF_ATTACH_CLIENT_DATA_VERSION_TOTAL_SIZE \ + } + // Version 1 of the eBPF extension data structures and their lengths. #define EBPF_ATTACH_PROVIDER_DATA_CURRENT_VERSION 1 #define EBPF_ATTACH_PROVIDER_DATA_CURRENT_VERSION_SIZE EBPF_SIZE_INCLUDING_FIELD(ebpf_attach_provider_data_t, link_type) diff --git a/libs/api/api_internal.h b/libs/api/api_internal.h index 5d051600c9..f504293333 100644 --- a/libs/api/api_internal.h +++ b/libs/api/api_internal.h @@ -30,6 +30,7 @@ typedef struct bpf_program bool pinned; const char* log_buffer; uint32_t log_buffer_size; + uint64_t flags; } ebpf_program_t; typedef struct bpf_map @@ -759,3 +760,15 @@ ebpf_api_thread_local_cleanup() noexcept; */ void ebpf_api_thread_local_initialize() noexcept; + +/** + * @brief Set the flags on a program + * + * @param[in] program_fd File descriptor for the program. + * @param[in] flags Flags to set on the program. + * + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_INVALID_ARGUMENT One or more parameters are wrong. + */ +_Must_inspect_result_ ebpf_result_t +ebpf_program_set_flags(fd_t program_fd, uint64_t flags) noexcept; \ No newline at end of file diff --git a/libs/api/ebpf_api.cpp b/libs/api/ebpf_api.cpp index 3e9c3ac30a..62397714d7 100644 --- a/libs/api/ebpf_api.cpp +++ b/libs/api/ebpf_api.cpp @@ -2131,6 +2131,12 @@ _initialize_ebpf_programs_native( result = EBPF_NO_MEMORY; goto Exit; } + if (program->flags != 0) { + result = ebpf_program_set_flags(program->fd, program->flags); + if (result != EBPF_SUCCESS) { + goto Exit; + } + } program->handle = program_handles[i]; program_handles[i] = ebpf_handle_invalid; program->program_type = info.type_uuid; @@ -3293,6 +3299,13 @@ _Requires_lock_not_held_(_ebpf_state_mutex) static ebpf_result_t program->fd = _create_file_descriptor_for_handle(program->handle); + if (program->flags != 0) { + result = ebpf_program_set_flags(program->fd, program->flags); + if (result != EBPF_SUCCESS) { + break; + } + } + // Populate load_info. ebpf_program_load_info load_info = {0}; load_info.object_name = const_cast(object->object_name); @@ -4673,3 +4686,20 @@ ebpf_api_thread_local_initialize() noexcept // Nothing to do. // Added for symmetry with ebpf_api_thread_local_cleanup. } + +_Must_inspect_result_ ebpf_result_t +ebpf_program_set_flags(fd_t program_fd, uint64_t flags) noexcept +{ + ebpf_handle_t program_handle = _get_handle_from_file_descriptor(program_fd); + if (program_handle == ebpf_handle_invalid) { + return EBPF_INVALID_FD; + } + + ebpf_operation_program_set_flags_request_t request; + request.header.id = ebpf_operation_id_t::EBPF_OPERATION_PROGRAM_SET_FLAGS; + request.header.length = sizeof(request); + request.program_handle = program_handle; + request.flags = flags; + + return win32_error_code_to_ebpf_result(invoke_ioctl(request)); +} diff --git a/libs/api/libbpf_program.cpp b/libs/api/libbpf_program.cpp index 2246855a3b..93d891974a 100644 --- a/libs/api/libbpf_program.cpp +++ b/libs/api/libbpf_program.cpp @@ -755,3 +755,19 @@ bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts* opts) return 0; } + +__u32 +bpf_program__flags(const struct bpf_program* prog) +{ + return static_cast(prog->flags); +} + +int +bpf_program__set_flags(struct bpf_program* prog, __u32 flags) +{ + if (prog->object->loaded) { + return libbpf_err(-EBUSY); + } + prog->flags = flags; + return 0; +} \ No newline at end of file diff --git a/libs/execution_context/ebpf_core.c b/libs/execution_context/ebpf_core.c index f2614f0a8e..359183264e 100644 --- a/libs/execution_context/ebpf_core.c +++ b/libs/execution_context/ebpf_core.c @@ -2114,6 +2114,30 @@ _ebpf_core_protocol_ring_buffer_map_write_data(_In_ const ebpf_operation_ring_bu EBPF_RETURN_RESULT(result); } +static ebpf_result_t +_ebpf_core_protocol_program_set_flags(_In_ const ebpf_operation_program_set_flags_request_t* request) +{ + EBPF_LOG_ENTRY(); + + ebpf_program_t* program = NULL; + + ebpf_result_t result = + EBPF_OBJECT_REFERENCE_BY_HANDLE(request->program_handle, EBPF_OBJECT_PROGRAM, (ebpf_core_object_t**)&program); + + if (result != EBPF_SUCCESS) { + goto Exit; + } + + ebpf_program_set_flags(program, request->flags); + +Exit: + if (program) { + EBPF_OBJECT_RELEASE_REFERENCE((ebpf_core_object_t*)program); + } + + EBPF_RETURN_RESULT(result); +} + static void* _ebpf_core_map_find_element(ebpf_map_t* map, const uint8_t* key) { @@ -2655,6 +2679,7 @@ static ebpf_protocol_handler_t _ebpf_protocol_handlers[] = { DECLARE_PROTOCOL_HANDLER_VARIABLE_REQUEST_FIXED_REPLY(map_delete_element_batch, keys, PROTOCOL_ALL_MODES), DECLARE_PROTOCOL_HANDLER_VARIABLE_REQUEST_VARIABLE_REPLY( map_get_next_key_value_batch, previous_key, data, PROTOCOL_ALL_MODES), + DECLARE_PROTOCOL_HANDLER_FIXED_REQUEST_NO_REPLY(program_set_flags, PROTOCOL_ALL_MODES), }; _Must_inspect_result_ ebpf_result_t diff --git a/libs/execution_context/ebpf_link.c b/libs/execution_context/ebpf_link.c index 8c471c4a11..a0d35a4187 100644 --- a/libs/execution_context/ebpf_link.c +++ b/libs/execution_context/ebpf_link.c @@ -254,7 +254,7 @@ _ebpf_link_client_detach_provider(void* client_binding_context) } static void -_ebpf_link_free(_Frees_ptr_ ebpf_core_object_t* object) +_ebpf_link_free(_In_ _Frees_ptr_ ebpf_core_object_t* object) { ebpf_link_t* link = (ebpf_link_t*)object; ebpf_free((void*)link->client_data.data); @@ -290,7 +290,9 @@ ebpf_link_create( ebpf_lock_state_t state = ebpf_lock_lock(&local_link->lock); lock_held = true; - local_link->client_data.header.size = context_data_length; + ebpf_extension_header_t header = EBPF_ATTACH_CLIENT_DATA_HEADER_VERSION; + + local_link->client_data.header = header; if (context_data_length > 0) { local_link->client_data.data = ebpf_allocate_with_tag(context_data_length, EBPF_POOL_TAG_LINK); @@ -299,6 +301,7 @@ ebpf_link_create( goto Exit; } memcpy((void*)local_link->client_data.data, context_data, context_data_length); + local_link->client_data.data_size = context_data_length; } local_link->module_id.Guid = module_id; @@ -365,6 +368,7 @@ ebpf_link_attach_program(_Inout_ ebpf_link_t* link, _Inout_ ebpf_program_t* prog link->program = program; link->program_type = ebpf_program_type_uuid(link->program); + link->client_data.prog_attach_flags = ebpf_program_get_flags(link->program); // Attach the program to the link. ebpf_program_attach_link(program, link); @@ -660,10 +664,10 @@ ebpf_link_get_info( // Copy any additional parameters. size_t size = sizeof(struct bpf_link_info) - FIELD_OFFSET(struct bpf_link_info, attach_data); - if ((link->client_data.header.size > 0) && (link->client_data.header.size <= size)) { - memcpy(&info->attach_data, link->client_data.data, link->client_data.header.size); - } + if ((link->client_data.data_size > 0) && (link->client_data.data_size <= size)) { + memcpy(&info->attach_data, link->client_data.data, link->client_data.data_size); + } ebpf_lock_unlock((ebpf_lock_t*)&link->lock, state); *info_size = sizeof(*info); diff --git a/libs/execution_context/ebpf_program.c b/libs/execution_context/ebpf_program.c index 2f633d9046..40388d1f4e 100644 --- a/libs/execution_context/ebpf_program.c +++ b/libs/execution_context/ebpf_program.c @@ -93,6 +93,7 @@ typedef struct _ebpf_program size_t helper_function_count; uint32_t* helper_function_ids; bool helper_ids_set; + uint64_t flags; // Lock protecting the fields below. ebpf_lock_t lock; @@ -2663,4 +2664,16 @@ ebpf_program_get_runtime_state(_In_ const void* program_context, _Outptr_ const // slot [0] contains the execution context state. ebpf_context_header_t* header = CONTAINING_RECORD(program_context, ebpf_context_header_t, context); *state = (ebpf_execution_context_state_t*)header->context_header[0]; -} \ No newline at end of file +} + +uint64_t +ebpf_program_get_flags(_In_ const ebpf_program_t* program) +{ + return program->flags; +} + +void +ebpf_program_set_flags(_Inout_ ebpf_program_t* program, uint64_t flags) +{ + program->flags = flags; +} diff --git a/libs/execution_context/ebpf_program.h b/libs/execution_context/ebpf_program.h index 4bb2ea580b..15e97ab46d 100644 --- a/libs/execution_context/ebpf_program.h +++ b/libs/execution_context/ebpf_program.h @@ -452,6 +452,24 @@ extern "C" ebpf_program_get_runtime_state( _In_ const void* program_context, _Outptr_ const ebpf_execution_context_state_t** state); + /** + * @brief Query the flags set on the program. + * + * @param[in] program The program to query. + * @return The flags set on the program. + */ + uint64_t + ebpf_program_get_flags(_In_ const ebpf_program_t* program); + + /** + * @brief Set the flags on the program. + * + * @param[in] program The program to set the flags on. + * @param[in] flags The flags to set on the program. + */ + void + ebpf_program_set_flags(_Inout_ ebpf_program_t* program, uint64_t flags); + #ifdef __cplusplus } #endif diff --git a/libs/execution_context/ebpf_protocol.h b/libs/execution_context/ebpf_protocol.h index d8607d48b2..a98ee6ce83 100644 --- a/libs/execution_context/ebpf_protocol.h +++ b/libs/execution_context/ebpf_protocol.h @@ -46,6 +46,7 @@ typedef enum _ebpf_operation_id EBPF_OPERATION_MAP_UPDATE_ELEMENT_BATCH, EBPF_OPERATION_MAP_DELETE_ELEMENT_BATCH, EBPF_OPERATION_MAP_GET_NEXT_KEY_VALUE_BATCH, + EBPF_OPERATION_PROGRAM_SET_FLAGS, } ebpf_operation_id_t; typedef enum _ebpf_code_type @@ -524,3 +525,10 @@ typedef struct _ebpf_operation_map_get_next_key_value_batch_reply // Data is a concatenation of key+value. uint8_t data[1]; } ebpf_operation_map_get_next_key_value_batch_reply_t; + +typedef struct _ebpf_operation_program_set_flags_request +{ + struct _ebpf_operation_header header; + ebpf_handle_t program_handle; + uint64_t flags; +} ebpf_operation_program_set_flags_request_t; diff --git a/libs/shared/shared_common.c b/libs/shared/shared_common.c index 7ba4bfdde5..1f94ed8d98 100644 --- a/libs/shared/shared_common.c +++ b/libs/shared/shared_common.c @@ -31,9 +31,8 @@ uint16_t _supported_ebpf_extension_version[] = { EBPF_PROGRAM_SECTION_INFORMATION_CURRENT_VERSION, }; -#define EBPF_ATTACH_PROVIDER_DATA_SIZE_0 \ - EBPF_OFFSET_OF(ebpf_attach_provider_data_t, link_type) + sizeof(enum bpf_link_type) -size_t _ebpf_attach_provider_data_supported_size[] = {EBPF_ATTACH_PROVIDER_DATA_SIZE_0}; +#define EBPF_ATTACH_PROVIDER_DATA_SIZE_1 EBPF_SIZE_INCLUDING_FIELD(ebpf_attach_provider_data_t, link_type) +size_t _ebpf_attach_provider_data_supported_size[] = {EBPF_ATTACH_PROVIDER_DATA_SIZE_1}; #define EBPF_PROGRAM_TYPE_DESCRIPTOR_SIZE_0 EBPF_OFFSET_OF(ebpf_program_type_descriptor_t, is_privileged) + sizeof(char) size_t _ebpf_program_type_descriptor_supported_size[] = {EBPF_PROGRAM_TYPE_DESCRIPTOR_SIZE_0}; diff --git a/tests/end_to_end/helpers.h b/tests/end_to_end/helpers.h index 31cd0091c9..77803df1dd 100644 --- a/tests/end_to_end/helpers.h +++ b/tests/end_to_end/helpers.h @@ -7,6 +7,7 @@ #include "ebpf_nethooks.h" #include "ebpf_platform.h" #include "ebpf_program_types.h" +#include "ebpf_windows.h" #include "net_ebpf_ext_program_info.h" #include "sample_ext_program_info.h" #include "usersim/ke.h" @@ -279,6 +280,12 @@ typedef class _single_instance_hook : public _hook_helper return batch_end_function(state); } + const ebpf_extension_data_t* + get_client_data() const + { + return client_data; + } + private: static NTSTATUS provider_attach_client_callback( @@ -301,6 +308,8 @@ typedef class _single_instance_hook : public _hook_helper hook->client_binding_context = client_binding_context; hook->nmr_binding_handle = nmr_binding_handle; hook->client_dispatch_table = (ebpf_extension_dispatch_table_t*)client_dispatch; + hook->client_data = + reinterpret_cast(client_registration_instance->NpiSpecificCharacteristics); *provider_binding_context = provider_context; *provider_dispatch = NULL; return STATUS_SUCCESS; diff --git a/tests/unit/libbpf_test.cpp b/tests/unit/libbpf_test.cpp index 2eacac62ef..d51e913cee 100644 --- a/tests/unit/libbpf_test.cpp +++ b/tests/unit/libbpf_test.cpp @@ -3771,3 +3771,68 @@ _utility_test(ebpf_execution_type_t execution_type) } DECLARE_ALL_TEST_CASES("utility_test", "[libbf]", _utility_test); + +static void +_program_flags_test(ebpf_execution_type_t execution_type) +{ + _test_helper_end_to_end test_helper; + const char dll_name[] = "utility_um.dll"; + const char obj_name[] = "utility.o"; + test_helper.initialize(); + single_instance_hook_t hook(EBPF_PROGRAM_TYPE_BIND, EBPF_ATTACH_TYPE_BIND); + REQUIRE(hook.initialize() == EBPF_SUCCESS); + program_info_provider_t sample_program_info; + REQUIRE(sample_program_info.initialize(EBPF_PROGRAM_TYPE_BIND) == EBPF_SUCCESS); + + const char* file_name = (execution_type == EBPF_EXECUTION_NATIVE ? dll_name : obj_name); + struct bpf_object* process_object = bpf_object__open(file_name); + REQUIRE(process_object != nullptr); + + // Set the flag on each program in the object. + struct bpf_program* program; + bpf_object__for_each_program(program, process_object) + { + // Set some flags on the program. The test attach provider does not use these flags. + REQUIRE(bpf_program__set_flags(program, 0xCCCCCCCC) == 0); + + // Get the flags and verify they are correct. + REQUIRE(bpf_program__flags(program) == 0xCCCCCCCC); + } + + // Load the program(s). + REQUIRE(bpf_object__load(process_object) == 0); + + // Verify that setting the flag after load fails. + bpf_object__for_each_program(program, process_object) + { + REQUIRE(bpf_program__set_flags(program, 0xCCCCCCCC) == -EBUSY); + } + + struct bpf_program* caller = bpf_object__find_program_by_name(process_object, "UtilityTest"); + REQUIRE(caller != nullptr); + + bpf_link_ptr link(bpf_program__attach(caller)); + REQUIRE(link != nullptr); + + auto client_data = hook.get_client_data(); + + REQUIRE(client_data != nullptr); + REQUIRE(client_data->header.version == EBPF_ATTACH_CLIENT_DATA_CURRENT_VERSION); + REQUIRE(client_data->prog_attach_flags == 0xCCCCCCCC); + + // Now run the ebpf program. + bind_md_t ctx = {0}; + ctx.operation = BIND_OPERATION_BIND; + + uint32_t result; + REQUIRE(hook.fire(&ctx, &result) == EBPF_SUCCESS); + + // Verify the result. + REQUIRE(result == 0); + + result = bpf_link__destroy(link.release()); + REQUIRE(result == 0); + bpf_object__close(process_object); +} + +DECLARE_ALL_TEST_CASES("program_flag_test", "[libbf]", _program_flags_test); \ No newline at end of file