Close native object handles in a work item in case of failure (#2395)

* fix

* fix

* fix

* fix analyze build

* cleanup

* code cleanup

* fix build break

* fix crash

* fix

* fix typo

* cr comments

* cr comments

* revert rundown change
This commit is contained in:
Anurag Saxena 2023-04-28 22:33:39 -07:00 коммит произвёл GitHub
Родитель 6c025904b3
Коммит 52b07b0164
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 176 добавлений и 26 удалений

Просмотреть файл

@ -2508,8 +2508,8 @@ _Requires_lock_not_held_(_ebpf_state_mutex) static ebpf_result_t
try {
if (result == EBPF_SUCCESS) {
std::unique_lock lock(_ebpf_state_mutex);
for (auto& map : object->maps) {
std::unique_lock lock(_ebpf_state_mutex);
_ebpf_maps.insert(std::pair<ebpf_handle_t, ebpf_map_t*>(map->map_handle, map));
}
}

Просмотреть файл

@ -54,6 +54,20 @@ typedef enum _ebpf_native_module_state
MODULE_STATE_UNLOADING,
} ebpf_native_module_state_t;
typedef struct _ebpf_native_handle_cleanup_information
{
size_t count_of_program_handles;
ebpf_handle_t* program_handles;
size_t count_of_map_handles;
ebpf_handle_t* map_handles;
} ebpf_native_handle_cleanup_info_t;
typedef struct _ebpf_native_handle_cleanup_context
{
ebpf_native_handle_cleanup_info_t* handle_information;
ebpf_preemptible_work_item_t* handle_cleanup_work_item;
} ebpf_native_handle_cleanup_context_t;
typedef struct _ebpf_native_module
{
ebpf_base_object_t base;
@ -69,7 +83,8 @@ typedef struct _ebpf_native_module
size_t program_count;
HANDLE nmr_binding_handle;
ebpf_list_entry_t list_entry;
ebpf_preemptible_work_item_t* cleanup_workitem;
ebpf_preemptible_work_item_t* cleanup_work_item;
ebpf_native_handle_cleanup_context_t handle_cleanup_context;
} ebpf_native_module_t;
static GUID _ebpf_native_npi_id = {/* c847aac8-a6f2-4b53-aea3-f4a94b9a80cb */
@ -152,7 +167,8 @@ _ebpf_native_is_map_in_map(_In_ const ebpf_native_map_t* map)
}
static void
_ebpf_native_clean_up_maps(_In_reads_(map_count) _Frees_ptr_ ebpf_native_map_t* maps, size_t map_count, bool unpin)
_ebpf_native_clean_up_maps(
_In_reads_(map_count) _Frees_ptr_ ebpf_native_map_t* maps, size_t map_count, bool unpin, bool close_handles)
{
for (uint32_t count = 0; count < map_count; count++) {
ebpf_native_map_t* map = &maps[count];
@ -170,8 +186,9 @@ _ebpf_native_clean_up_maps(_In_reads_(map_count) _Frees_ptr_ ebpf_native_map_t*
ebpf_free(map->pin_path.value);
#pragma warning(pop)
}
if (map->handle != ebpf_handle_invalid) {
if (close_handles && map->handle != ebpf_handle_invalid) {
ebpf_assert_success(ebpf_handle_close(map->handle));
map->handle = ebpf_handle_invalid;
}
}
@ -179,7 +196,8 @@ _ebpf_native_clean_up_maps(_In_reads_(map_count) _Frees_ptr_ ebpf_native_map_t*
}
static void
_ebpf_native_clean_up_programs(_In_reads_(count_of_programs) ebpf_native_program_t* programs, size_t count_of_programs)
_ebpf_native_clean_up_programs(
_In_reads_(count_of_programs) ebpf_native_program_t* programs, size_t count_of_programs, bool close_handles)
{
for (uint32_t i = 0; i < count_of_programs; i++) {
if (programs[i].handle != ebpf_handle_invalid) {
@ -188,7 +206,10 @@ _ebpf_native_clean_up_programs(_In_reads_(count_of_programs) ebpf_native_program
programs[i].handle, EBPF_OBJECT_PROGRAM, (ebpf_core_object_t**)&program_object));
ebpf_assert_success(ebpf_program_register_for_helper_changes(program_object, NULL, NULL));
ebpf_object_release_reference((ebpf_core_object_t*)program_object);
ebpf_assert_success(ebpf_handle_close(programs[i].handle));
if (close_handles) {
ebpf_assert_success(ebpf_handle_close(programs[i].handle));
programs[i].handle = ebpf_handle_invalid;
}
}
ebpf_free(programs[i].addresses_changed_callback_context);
programs[i].addresses_changed_callback_context = NULL;
@ -200,8 +221,8 @@ _ebpf_native_clean_up_programs(_In_reads_(count_of_programs) ebpf_native_program
static void
_ebpf_native_clean_up_module(_In_ _Post_invalid_ ebpf_native_module_t* module)
{
_ebpf_native_clean_up_maps(module->maps, module->map_count, false);
_ebpf_native_clean_up_programs(module->programs, module->program_count);
_ebpf_native_clean_up_maps(module->maps, module->map_count, false, true);
_ebpf_native_clean_up_programs(module->programs, module->program_count, true);
module->maps = NULL;
module->map_count = 0;
@ -210,7 +231,7 @@ _ebpf_native_clean_up_module(_In_ _Post_invalid_ ebpf_native_module_t* module)
// Note: Do not free module->service_name here explicitly.
// It will be freed automatically when workitem is freed.
ebpf_free_preemptible_work_item(module->cleanup_workitem);
ebpf_free_preemptible_work_item(module->cleanup_work_item);
ebpf_lock_destroy(&module->lock);
@ -236,8 +257,8 @@ _Requires_lock_held_(module->lock) static ebpf_result_t _ebpf_native_unload(_Ino
module->state = MODULE_STATE_UNLOADING;
// Queue pre-allocated work item to unload the driver.
work_item = module->cleanup_workitem;
module->cleanup_workitem = NULL;
work_item = module->cleanup_work_item;
module->cleanup_work_item = NULL;
module->service_name = NULL;
ebpf_queue_preemptible_work_item(work_item);
@ -848,7 +869,11 @@ _ebpf_native_create_maps(_Inout_ ebpf_native_module_t* module)
Done:
if (result != EBPF_SUCCESS) {
_ebpf_native_clean_up_maps(module->maps, module->map_count, true);
// Copy the handles in the cleanup context.
for (size_t i = 0; i < module->map_count; i++) {
module->handle_cleanup_context.handle_information->map_handles[i] = module->maps[i].handle;
}
_ebpf_native_clean_up_maps(module->maps, module->map_count, true, false);
module->maps = NULL;
module->map_count = 0;
}
@ -1164,7 +1189,11 @@ _ebpf_native_load_programs(_Inout_ ebpf_native_module_t* module)
}
if (result != EBPF_SUCCESS) {
_ebpf_native_clean_up_programs(module->programs, module->program_count);
// Copy the handles in the cleanup context.
for (size_t i = 0; i < module->program_count; i++) {
module->handle_cleanup_context.handle_information->program_handles[i] = module->programs[i].handle;
}
_ebpf_native_clean_up_programs(module->programs, module->program_count, false);
module->programs = NULL;
module->program_count = 0;
}
@ -1195,6 +1224,97 @@ _ebpf_native_get_count_of_programs(_In_ const ebpf_native_module_t* module)
return count_of_programs;
}
static void
_ebpf_native_close_handles_work_item(_In_opt_ const void* context)
{
if (context == NULL) {
return;
}
ebpf_native_handle_cleanup_info_t* handle_info = (ebpf_native_handle_cleanup_info_t*)context;
for (uint32_t i = 0; i < handle_info->count_of_program_handles; i++) {
if (handle_info->program_handles[i] != ebpf_handle_invalid) {
(void)ebpf_handle_close(handle_info->program_handles[i]);
handle_info->program_handles[i] = ebpf_handle_invalid;
}
}
for (uint32_t i = 0; i < handle_info->count_of_map_handles; i++) {
if (handle_info->map_handles[i] != ebpf_handle_invalid) {
(void)ebpf_handle_close(handle_info->map_handles[i]);
handle_info->map_handles[i] = ebpf_handle_invalid;
}
}
ebpf_free(handle_info->program_handles);
ebpf_free(handle_info->map_handles);
}
static void
_ebpf_native_clean_up_handle_cleanup_context(_Inout_ ebpf_native_handle_cleanup_context_t* cleanup_context)
{
if (cleanup_context == NULL) {
return;
}
if (cleanup_context->handle_information != NULL) {
ebpf_free(cleanup_context->handle_information->map_handles);
ebpf_free(cleanup_context->handle_information->program_handles);
}
ebpf_free_preemptible_work_item(cleanup_context->handle_cleanup_work_item);
}
static ebpf_result_t
_ebpf_native_initialize_handle_cleanup_context(
size_t program_handle_count, size_t map_handle_count, _Inout_ ebpf_native_handle_cleanup_context_t* cleanup_context)
{
ebpf_result_t result = EBPF_SUCCESS;
memset(cleanup_context, 0, sizeof(ebpf_native_handle_cleanup_context_t));
cleanup_context->handle_information =
(ebpf_native_handle_cleanup_info_t*)ebpf_allocate(sizeof(ebpf_native_handle_cleanup_info_t));
if (cleanup_context->handle_information == NULL) {
result = EBPF_NO_MEMORY;
goto Done;
}
if (map_handle_count > 0) {
cleanup_context->handle_information->map_handles =
(ebpf_handle_t*)ebpf_allocate(sizeof(ebpf_handle_t) * map_handle_count);
if (cleanup_context->handle_information->map_handles == NULL) {
result = EBPF_NO_MEMORY;
goto Done;
}
}
cleanup_context->handle_information->count_of_map_handles = map_handle_count;
cleanup_context->handle_information->program_handles =
(ebpf_handle_t*)ebpf_allocate(sizeof(ebpf_handle_t) * program_handle_count);
if (cleanup_context->handle_information->program_handles == NULL) {
result = EBPF_NO_MEMORY;
goto Done;
}
cleanup_context->handle_information->count_of_program_handles = program_handle_count;
for (size_t i = 0; i < map_handle_count; i++) {
cleanup_context->handle_information->map_handles[i] = ebpf_handle_invalid;
}
for (size_t i = 0; i < program_handle_count; i++) {
cleanup_context->handle_information->program_handles[i] = ebpf_handle_invalid;
}
result = ebpf_allocate_preemptible_work_item(
&cleanup_context->handle_cleanup_work_item,
_ebpf_native_close_handles_work_item,
cleanup_context->handle_information);
Done:
if (result != EBPF_SUCCESS) {
_ebpf_native_clean_up_handle_cleanup_context(cleanup_context);
}
return result;
}
_Must_inspect_result_ ebpf_result_t
ebpf_native_load(
_In_reads_(service_name_length) const wchar_t* service_name,
@ -1212,8 +1332,8 @@ ebpf_native_load(
ebpf_native_module_t* module = NULL;
ebpf_native_module_t** existing_module = NULL;
wchar_t* local_service_name = NULL;
ebpf_handle_t local_module_hande = ebpf_handle_invalid;
ebpf_preemptible_work_item_t* cleanup_workitem = NULL;
ebpf_handle_t local_module_handle = ebpf_handle_invalid;
ebpf_preemptible_work_item_t* cleanup_work_item = NULL;
local_service_name = ebpf_allocate_with_tag((size_t)service_name_length + 2, EBPF_POOL_TAG_NATIVE);
if (local_service_name == NULL) {
@ -1222,7 +1342,7 @@ ebpf_native_load(
}
memcpy(local_service_name, (uint8_t*)service_name, service_name_length);
result = ebpf_allocate_preemptible_work_item(&cleanup_workitem, _ebpf_native_unload_work_item, local_service_name);
result = ebpf_allocate_preemptible_work_item(&cleanup_work_item, _ebpf_native_unload_work_item, local_service_name);
if (result != EBPF_SUCCESS) {
ebpf_free(local_service_name);
goto Done;
@ -1278,8 +1398,9 @@ ebpf_native_load(
module->state = MODULE_STATE_INITIALIZING;
ebpf_lock_unlock(&module->lock, state);
// Create handle for the native module.
result = ebpf_handle_create(&local_module_hande, (ebpf_base_object_t*)module);
// Create handle for the native module. This should be the last step in initialization which can fail.
// Else, we can have a case where the same thread enters epoch recursively.
result = ebpf_handle_create(&local_module_handle, (ebpf_base_object_t*)module);
if (result != EBPF_SUCCESS) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
@ -1292,17 +1413,18 @@ ebpf_native_load(
state = ebpf_lock_lock(&module->lock);
module->state = MODULE_STATE_INITIALIZED;
module->service_name = local_service_name;
module->cleanup_workitem = cleanup_workitem;
module->cleanup_work_item = cleanup_work_item;
cleanup_workitem = NULL;
cleanup_work_item = NULL;
local_service_name = NULL;
ebpf_lock_unlock(&module->lock, state);
// Get map and program count;
*count_of_maps = _ebpf_native_get_count_of_maps(module);
*count_of_programs = _ebpf_native_get_count_of_programs(module);
*module_handle = local_module_hande;
local_module_hande = ebpf_handle_invalid;
*module_handle = local_module_handle;
local_module_handle = ebpf_handle_invalid;
Done:
if (table_lock_acquired) {
@ -1310,10 +1432,10 @@ Done:
table_lock_acquired = false;
}
if (result != EBPF_SUCCESS) {
ebpf_free_preemptible_work_item(cleanup_workitem);
ebpf_free_preemptible_work_item(cleanup_work_item);
}
if (local_module_hande != ebpf_handle_invalid) {
ebpf_assert_success(ebpf_handle_close(local_module_hande));
if (local_module_handle != ebpf_handle_invalid) {
ebpf_assert_success(ebpf_handle_close(local_module_handle));
}
EBPF_RETURN_RESULT(result);
@ -1338,6 +1460,7 @@ ebpf_native_load_programs(
wchar_t* local_service_name = NULL;
bool module_referenced = false;
bool maps_created = false;
bool cleanup_context_created = false;
// Find the native entry in hash table.
state = ebpf_lock_lock(&_ebpf_native_client_table_lock);
@ -1392,6 +1515,19 @@ ebpf_native_load_programs(
ebpf_lock_unlock(&_ebpf_native_client_table_lock, state);
lock_acquired = false;
// Create handle cleanup context and work item. This is used to close the handles in a work item in case of failure.
result = _ebpf_native_initialize_handle_cleanup_context(
count_of_program_handles, count_of_map_handles, &module->handle_cleanup_context);
if (result != EBPF_SUCCESS) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_VERBOSE,
EBPF_TRACELOG_KEYWORD_NATIVE,
"ebpf_native_load_programs: _ebpf_native_initialize_handle_cleanup_context failed",
*module_id);
goto Done;
}
cleanup_context_created = true;
// Create maps.
result = _ebpf_native_create_maps(module);
if (result != EBPF_SUCCESS) {
@ -1447,12 +1583,26 @@ Done:
}
if (result != EBPF_SUCCESS) {
if (maps_created) {
_ebpf_native_clean_up_maps(module->maps, module->map_count, true);
for (uint32_t i = 0; i < module->map_count; i++) {
module->handle_cleanup_context.handle_information->map_handles[i] = module->maps[i].handle;
}
_ebpf_native_clean_up_maps(module->maps, module->map_count, true, false);
module->maps = NULL;
module->map_count = 0;
}
ebpf_free(local_service_name);
if (cleanup_context_created) {
// Queue work item to close map and program handles.
ebpf_queue_preemptible_work_item(module->handle_cleanup_context.handle_cleanup_work_item);
module->handle_cleanup_context.handle_cleanup_work_item = NULL;
module->handle_cleanup_context.handle_information = NULL;
}
} else {
// Success case. No need to close program and map handles. Clean up handle cleanup context.
_ebpf_native_clean_up_handle_cleanup_context(&module->handle_cleanup_context);
}
if (module_referenced) {
ebpf_native_release_reference(module);
module_referenced = false;