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:
Родитель
6c025904b3
Коммит
52b07b0164
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче