diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index d3f550906d..e0bb6410c9 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -144,6 +144,29 @@ jobs: with: msbuild-architecture: x64 + - name: Cache chocolatey packages + if: steps.skip_check.outputs.should_skip != 'true' + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 + env: + cache-name: cache-choco-packages + with: + path: C:\ProgramData\chocolatey + key: ${{ runner.os }}-choco-llvm-18.1.8 + + - name: Install LLVM 18.1.8 + if: steps.skip_check.outputs.should_skip != 'true' + run: | + # Install LLVM 18.1.8 to ensure consistent version across runners + try { + choco install llvm --version=18.1.8 --allow-downgrade -y --force + # Add installed LLVM to PATH first so it takes precedence + echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + Write-Host "Successfully installed LLVM 18.1.8" + } catch { + Write-Warning "Failed to install LLVM 18.1.8 via chocolatey: $($_.Exception.Message)" + Write-Host "Continuing with pre-installed LLVM version" + } + - name: Add Visual Studio LLVM to path if: steps.skip_check.outputs.should_skip != 'true' run: | @@ -158,7 +181,7 @@ jobs: if: steps.skip_check.outputs.should_skip != 'true' shell: cmd run: - '"c:\Program Files\llvm\bin\clang.exe" --version' + '"C:\Program Files\LLVM\bin\clang.exe" --version' - name: Cache nuget packages if: steps.skip_check.outputs.should_skip != 'true' diff --git a/libs/execution_context/ebpf_maps.c b/libs/execution_context/ebpf_maps.c index f9cfa0e513..30298caeaf 100644 --- a/libs/execution_context/ebpf_maps.c +++ b/libs/execution_context/ebpf_maps.c @@ -409,7 +409,11 @@ typedef struct _ebpf_map_metadata_table void (*delete_map)(_In_ _Post_invalid_ ebpf_core_map_t* map); ebpf_result_t (*associate_program)(_Inout_ ebpf_map_t* map, _In_ const ebpf_program_t* program); ebpf_result_t (*find_entry)( - _Inout_ ebpf_core_map_t* map, _In_opt_ const uint8_t* key, bool delete_on_success, _Outptr_ uint8_t** data); + _Inout_ ebpf_core_map_t* map, + _In_opt_ const uint8_t* key, + bool delete_on_success, + bool helper_function, + _Outptr_ uint8_t** data); ebpf_core_object_t* (*get_object_from_entry)(_Inout_ ebpf_core_map_t* map, _In_ const uint8_t* key); ebpf_result_t (*update_entry)( _Inout_ ebpf_core_map_t* map, _In_opt_ const uint8_t* key, _In_ const uint8_t* value, ebpf_map_option_t option); @@ -524,9 +528,15 @@ _delete_array_map(_In_ _Post_invalid_ ebpf_core_map_t* map) static ebpf_result_t _find_array_map_entry( - _Inout_ ebpf_core_map_t* map, _In_opt_ const uint8_t* key, bool delete_on_success, _Outptr_ uint8_t** data) + _Inout_ ebpf_core_map_t* map, + _In_opt_ const uint8_t* key, + bool delete_on_success, + bool helper_function, + _Outptr_ uint8_t** data) { uint32_t key_value; + UNREFERENCED_PARAMETER(helper_function); + if (!map || !key || delete_on_success) { return EBPF_INVALID_ARGUMENT; } @@ -544,11 +554,15 @@ _find_array_map_entry( static ebpf_result_t _find_array_map_entry_with_reference( - _Inout_ ebpf_core_map_t* map, _In_opt_ const uint8_t* key, bool delete_on_success, _Outptr_ uint8_t** data) + _Inout_ ebpf_core_map_t* map, + _In_opt_ const uint8_t* key, + bool delete_on_success, + bool helper_function, + _Outptr_ uint8_t** data) { ebpf_assert(map->ebpf_map_definition.value_size == sizeof(ebpf_id_t)); - ebpf_result_t result = _find_array_map_entry(map, key, delete_on_success, data); + ebpf_result_t result = _find_array_map_entry(map, key, delete_on_success, helper_function, data); if (result != EBPF_SUCCESS) { return result; @@ -960,7 +974,7 @@ _delete_array_map_entry_with_reference( uint8_t* entry; ebpf_core_object_map_t* object_map = EBPF_FROM_FIELD(ebpf_core_object_map_t, core_map, map); ebpf_lock_state_t lock_state = ebpf_lock_lock(&object_map->lock); - result = _find_array_map_entry(map, key, false, &entry); + result = _find_array_map_entry(map, key, false, true, &entry); if (result == EBPF_SUCCESS) { ebpf_id_t id = *(ebpf_id_t*)entry; if (id) { @@ -1001,7 +1015,7 @@ _get_object_from_array_map_entry(_Inout_ ebpf_core_map_t* map, _In_ const uint8_ ebpf_core_object_t* object = NULL; uint8_t* value = NULL; - if (_find_array_map_entry(map, (uint8_t*)&index, false, &value) == EBPF_SUCCESS) { + if (_find_array_map_entry(map, (uint8_t*)&index, false, true, &value) == EBPF_SUCCESS) { ebpf_id_t id = *(ebpf_id_t*)&map->data[index * map->ebpf_map_definition.value_size]; ebpf_object_type_t value_type = (map->ebpf_map_definition.type == BPF_MAP_TYPE_PROG_ARRAY) ? EBPF_OBJECT_PROGRAM : EBPF_OBJECT_MAP; @@ -1339,12 +1353,21 @@ _uninitialize_lru_entry(_Inout_ ebpf_core_lru_map_t* map, _Inout_ ebpf_lru_entry static void _lru_hash_table_notification( - _In_ void* context, _In_ ebpf_hash_table_notification_type_t type, _In_ const uint8_t* key, _In_ uint8_t* value) + _In_ void* context, + _In_ ebpf_hash_table_notification_type_t type, + _In_opt_ const void* operation_context, + _In_ const uint8_t* key, + _In_ uint8_t* value) { ebpf_core_lru_map_t* lru_map = (ebpf_core_lru_map_t*)context; ebpf_lru_entry_t* entry = (ebpf_lru_entry_t*)_get_supplemental_value(&lru_map->core_map, value); // Map the current CPU to a partition. uint32_t partition = ebpf_get_current_cpu() % lru_map->partition_count; + bool helper_function = true; + if (operation_context) { + helper_function = *(bool*)operation_context; + } + switch (type) { case EBPF_HASH_TABLE_NOTIFICATION_TYPE_ALLOCATE: _initialize_lru_entry(lru_map, entry, partition, key); @@ -1353,7 +1376,12 @@ _lru_hash_table_notification( _uninitialize_lru_entry(lru_map, entry); break; case EBPF_HASH_TABLE_NOTIFICATION_TYPE_USE: - _insert_into_hot_list(lru_map, partition, entry); + if (helper_function) { + // Only when the entry is accessed via helper function, insert it into the hot list. + // This is to avoid the hot list from being filled with entries that are accessed via the user mode APIs. + _insert_into_hot_list(lru_map, partition, entry); + } + // _insert_into_hot_list(lru_map, partition, entry); break; default: ebpf_assert(!"Invalid notification type"); @@ -1567,14 +1595,19 @@ _reap_oldest_map_entry(_Inout_ ebpf_core_map_t* map) static ebpf_result_t _find_hash_map_entry( - _Inout_ ebpf_core_map_t* map, _In_opt_ const uint8_t* key, bool delete_on_success, _Outptr_ uint8_t** data) + _Inout_ ebpf_core_map_t* map, + _In_opt_ const uint8_t* key, + bool delete_on_success, + bool helper_function, + _Outptr_ uint8_t** data) { uint8_t* value = NULL; if (!map || !key) { return EBPF_INVALID_ARGUMENT; } - if (ebpf_hash_table_find((ebpf_hash_table_t*)map->data, key, &value) != EBPF_SUCCESS) { + if (ebpf_hash_table_find_with_context((ebpf_hash_table_t*)map->data, key, &helper_function, &value) != + EBPF_SUCCESS) { value = NULL; } @@ -1604,7 +1637,7 @@ _get_object_from_hash_map_entry(_In_ ebpf_core_map_t* map, _In_ const uint8_t* k { ebpf_core_object_t* object = NULL; uint8_t* value = NULL; - if (_find_hash_map_entry(map, key, false, &value) == EBPF_SUCCESS) { + if (_find_hash_map_entry(map, key, false, true, &value) == EBPF_SUCCESS) { ebpf_id_t id = *(ebpf_id_t*)value; if (ebpf_object_pointer_by_id(id, EBPF_OBJECT_MAP, &object) != EBPF_SUCCESS) { object = NULL; @@ -1772,7 +1805,7 @@ _delete_map_hash_map_entry(_Inout_ ebpf_core_map_t* map, _In_ const uint8_t* key ebpf_lock_state_t lock_state = ebpf_lock_lock(&object_map->lock); uint8_t* value = NULL; - ebpf_result_t result = _find_hash_map_entry(map, key, true, &value); + ebpf_result_t result = _find_hash_map_entry(map, key, true, true, &value); if (result == EBPF_SUCCESS) { ebpf_id_t id = *(ebpf_id_t*)value; if (id) { @@ -1848,12 +1881,12 @@ _update_entry_per_cpu( const ebpf_map_metadata_table_t* table = ebpf_map_get_table(map->ebpf_map_definition.type); uint8_t* target; - if (table->find_entry(map, key, false, &target) != EBPF_SUCCESS) { + if (table->find_entry(map, key, false, true, &target) != EBPF_SUCCESS) { ebpf_result_t return_value = table->update_entry(map, key, NULL, option); if (return_value != EBPF_SUCCESS) { return return_value; } - if (table->find_entry(map, key, false, &target) != EBPF_SUCCESS) { + if (table->find_entry(map, key, false, true, &target) != EBPF_SUCCESS) { return EBPF_NO_MEMORY; } } @@ -1918,7 +1951,11 @@ _create_lpm_map( static ebpf_result_t _find_lpm_map_entry( - _Inout_ ebpf_core_map_t* map, _In_opt_ const uint8_t* key, bool delete_on_success, _Outptr_ uint8_t** data) + _Inout_ ebpf_core_map_t* map, + _In_opt_ const uint8_t* key, + bool delete_on_success, + bool helper_function, + _Outptr_ uint8_t** data) { if (!map || !key || delete_on_success) { return EBPF_INVALID_ARGUMENT; @@ -1939,7 +1976,7 @@ _find_lpm_map_entry( ebpf_bitmap_start_reverse_search_at((ebpf_bitmap_t*)trie_map->data, &cursor, original_prefix_length); lpm_key->prefix_length = (uint32_t)ebpf_bitmap_reverse_search_next_bit(&cursor); while (lpm_key->prefix_length != MAXUINT32) { - if (_find_hash_map_entry(map, key, false, &value) == EBPF_SUCCESS) { + if (_find_hash_map_entry(map, key, false, helper_function, &value) == EBPF_SUCCESS) { break; } lpm_key->prefix_length = (uint32_t)ebpf_bitmap_reverse_search_next_bit(&cursor); @@ -2066,7 +2103,11 @@ _delete_circular_map(_In_ _Post_invalid_ ebpf_core_map_t* map) static ebpf_result_t _find_circular_map_entry( - _Inout_ ebpf_core_map_t* map, _In_opt_ const uint8_t* key, bool delete_on_success, _Outptr_ uint8_t** data) + _Inout_ ebpf_core_map_t* map, + _In_opt_ const uint8_t* key, + bool delete_on_success, + bool helper_function, + _Outptr_ uint8_t** data) { if (!map) { return EBPF_INVALID_ARGUMENT; @@ -2075,6 +2116,7 @@ _find_circular_map_entry( // Queue uses no key, but the caller always passes in a non-null pointer (with a 0 key size) // so we cannot require key to be null. UNREFERENCED_PARAMETER(key); + UNREFERENCED_PARAMETER(helper_function); ebpf_core_circular_map_t* circular_map = EBPF_FROM_FIELD(ebpf_core_circular_map_t, core_map, map); ebpf_lock_state_t state = ebpf_lock_lock(&circular_map->lock); @@ -3136,8 +3178,9 @@ ebpf_map_find_entry( return_value = (uint8_t*)object; } } else { - ebpf_result_t result = - table->find_entry(map, key, flags & EBPF_MAP_FIND_FLAG_DELETE ? true : false, &return_value); + bool delete_on_find = (flags & EBPF_MAP_FIND_FLAG_DELETE) ? true : false; + bool helper = (flags & EBPF_MAP_FLAG_HELPER) ? true : false; + ebpf_result_t result = table->find_entry(map, key, delete_on_find, helper, &return_value); if (result != EBPF_SUCCESS) { return result; } @@ -3438,6 +3481,8 @@ ebpf_map_pop_entry(_Inout_ ebpf_map_t* map, size_t value_size, _Out_writes_(valu return EBPF_INVALID_ARGUMENT; } + bool helper_function = (flags & EBPF_MAP_FLAG_HELPER) ? true : false; + const ebpf_map_metadata_table_t* table = ebpf_map_get_table(map->ebpf_map_definition.type); if (table->find_entry == NULL) { @@ -3449,7 +3494,7 @@ ebpf_map_pop_entry(_Inout_ ebpf_map_t* map, size_t value_size, _Out_writes_(valu return EBPF_OPERATION_NOT_SUPPORTED; } - ebpf_result_t result = table->find_entry(map, NULL, true, &return_value); + ebpf_result_t result = table->find_entry(map, NULL, true, helper_function, &return_value); if (result != EBPF_SUCCESS) { return result; } @@ -3466,6 +3511,8 @@ ebpf_map_peek_entry(_Inout_ ebpf_map_t* map, size_t value_size, _Out_writes_(val return EBPF_INVALID_ARGUMENT; } + bool helper_function = (flags & EBPF_MAP_FLAG_HELPER) ? true : false; + const ebpf_map_metadata_table_t* table = ebpf_map_get_table(map->ebpf_map_definition.type); if (table->find_entry == NULL) { @@ -3477,7 +3524,7 @@ ebpf_map_peek_entry(_Inout_ ebpf_map_t* map, size_t value_size, _Out_writes_(val return EBPF_OPERATION_NOT_SUPPORTED; } - ebpf_result_t result = table->find_entry(map, NULL, false, &return_value); + ebpf_result_t result = table->find_entry(map, NULL, false, helper_function, &return_value); if (result != EBPF_SUCCESS) { return result; } diff --git a/libs/runtime/ebpf_error.c b/libs/runtime/ebpf_error.c index 80c73e2841..964fea548b 100644 --- a/libs/runtime/ebpf_error.c +++ b/libs/runtime/ebpf_error.c @@ -55,5 +55,11 @@ static const NTSTATUS _ebpf_result_mapping[] = { NTSTATUS ebpf_result_to_ntstatus(ebpf_result_t result) { - return (result < EBPF_RESULT_COUNT) ? _ebpf_result_mapping[result] : STATUS_UNSUCCESSFUL; + if (result < 0) { + return STATUS_UNSUCCESSFUL; + } + if (result > ARRAYSIZE(_ebpf_result_mapping)) { + return STATUS_UNSUCCESSFUL; + } + return _ebpf_result_mapping[result]; } diff --git a/libs/runtime/ebpf_hash_table.c b/libs/runtime/ebpf_hash_table.c index 1437c41699..87e939e301 100644 --- a/libs/runtime/ebpf_hash_table.c +++ b/libs/runtime/ebpf_hash_table.c @@ -624,7 +624,7 @@ _ebpf_hash_table_replace_bucket( } if (hash_table->notification_callback) { hash_table->notification_callback( - hash_table->notification_context, EBPF_HASH_TABLE_NOTIFICATION_TYPE_ALLOCATE, key, new_data); + hash_table->notification_context, EBPF_HASH_TABLE_NOTIFICATION_TYPE_ALLOCATE, NULL, key, new_data); } } @@ -695,11 +695,11 @@ _ebpf_hash_table_replace_bucket( if (hash_table->notification_callback) { if (new_data) { hash_table->notification_callback( - hash_table->notification_context, EBPF_HASH_TABLE_NOTIFICATION_TYPE_FREE, key, new_data); + hash_table->notification_context, EBPF_HASH_TABLE_NOTIFICATION_TYPE_FREE, NULL, key, new_data); } if (old_data) { hash_table->notification_callback( - hash_table->notification_context, EBPF_HASH_TABLE_NOTIFICATION_TYPE_FREE, key, old_data); + hash_table->notification_context, EBPF_HASH_TABLE_NOTIFICATION_TYPE_FREE, NULL, key, old_data); } } @@ -801,7 +801,11 @@ ebpf_hash_table_destroy(_In_opt_ _Post_ptr_invalid_ ebpf_hash_table_t* hash_tabl } _Must_inspect_result_ ebpf_result_t -ebpf_hash_table_find(_In_ const ebpf_hash_table_t* hash_table, _In_ const uint8_t* key, _Outptr_ uint8_t** value) +ebpf_hash_table_find_with_context( + _In_ const ebpf_hash_table_t* hash_table, + _In_ const uint8_t* key, + _In_opt_ const void* context, + _Outptr_ uint8_t** value) { ebpf_result_t retval; uint32_t bucket_index; @@ -839,13 +843,19 @@ ebpf_hash_table_find(_In_ const ebpf_hash_table_t* hash_table, _In_ const uint8_ *value = data; if (hash_table->notification_callback) { hash_table->notification_callback( - hash_table->notification_context, EBPF_HASH_TABLE_NOTIFICATION_TYPE_USE, key, data); + hash_table->notification_context, EBPF_HASH_TABLE_NOTIFICATION_TYPE_USE, context, key, data); } retval = EBPF_SUCCESS; Done: return retval; } +_Must_inspect_result_ ebpf_result_t +ebpf_hash_table_find(_In_ const ebpf_hash_table_t* hash_table, _In_ const uint8_t* key, _Outptr_ uint8_t** value) +{ + return ebpf_hash_table_find_with_context(hash_table, key, NULL, value); +} + _Must_inspect_result_ ebpf_result_t ebpf_hash_table_update( _Inout_ ebpf_hash_table_t* hash_table, diff --git a/libs/runtime/ebpf_hash_table.h b/libs/runtime/ebpf_hash_table.h index 68dd24004c..8005e67b21 100644 --- a/libs/runtime/ebpf_hash_table.h +++ b/libs/runtime/ebpf_hash_table.h @@ -31,6 +31,7 @@ extern "C" typedef void (*ebpf_hash_table_notification_function)( _Inout_ void* context, _In_ ebpf_hash_table_notification_type_t type, + _In_opt_ const void* operation_context, //< Context passed to ebpf_hash_table_* functions. _In_ const uint8_t* key, _Inout_ uint8_t* value); @@ -101,6 +102,13 @@ extern "C" _Must_inspect_result_ ebpf_result_t ebpf_hash_table_find(_In_ const ebpf_hash_table_t* hash_table, _In_ const uint8_t* key, _Outptr_ uint8_t** value); + _Must_inspect_result_ ebpf_result_t + ebpf_hash_table_find_with_context( + _In_ const ebpf_hash_table_t* hash_table, + _In_ const uint8_t* key, + _In_opt_ const void* context, + _Outptr_ uint8_t** value); + /** * @brief Insert or update an entry in the hash table. * diff --git a/tests/end_to_end/netsh_test.cpp b/tests/end_to_end/netsh_test.cpp index 6c4530336c..8707f8c88d 100644 --- a/tests/end_to_end/netsh_test.cpp +++ b/tests/end_to_end/netsh_test.cpp @@ -354,14 +354,21 @@ TEST_CASE("show sections cgroup_sock_addr.sys", "[netsh][sections]") _run_netsh_command(handle_ebpf_show_sections, L"cgroup_sock_addr.sys", nullptr, nullptr, &result); REQUIRE(result == NO_ERROR); + // Old code size is for MSVC 2022 version 17.13.7 + // Code size is for MSVC 2022 version 17.14.0 and later. + #if defined(_M_X64) && defined(NDEBUG) - const int code_size[] = {333, 350, 333, 350}; + const int old_code_size[] = {339, 363, 339, 363}; + const int code_size[] = {339, 363, 339, 363}; #elif defined(_M_X64) && !defined(NDEBUG) - const int code_size[] = {961, 1036, 961, 1036}; + const int old_code_size[] = {961, 1036, 961, 1036}; + const int code_size[] = {1089, 1224, 1089, 1224}; #elif defined(_M_ARM64) && defined(NDEBUG) - const int code_size[] = {308, 324, 308, 324}; + const int old_code_size[] = {328, 344, 328, 344}; + const int code_size[] = {328, 352, 328, 352}; #elif defined(_M_ARM64) && !defined(NDEBUG) - const int code_size[] = {1044, 1176, 1044, 1176}; + const int old_code_size[] = {1132, 1288, 1132, 1288}; + const int code_size[] = {1132, 1288, 1132, 1288}; #else #error "Unsupported architecture" #endif @@ -381,9 +388,21 @@ TEST_CASE("show sections cgroup_sock_addr.sys", "[netsh][sections]") " hash 56 4 1 egress_connection_policy_map\n" " hash 56 4 1 ingress_connection_policy_map\n" " hash 56 8 1000 socket_cookie_map\n"; - REQUIRE( - output == - std::vformat(expected_output, std::make_format_args(code_size[0], code_size[1], code_size[2], code_size[3]))); + + bool output_matches = + (output == + std::vformat( + expected_output, std::make_format_args(code_size[0], code_size[1], code_size[2], code_size[3])) || + output == std::vformat( + expected_output, + std::make_format_args(old_code_size[0], old_code_size[1], old_code_size[2], old_code_size[3]))); + + if (!output_matches) { + std::cerr << "Expected output:\n" << expected_output << "\n"; + std::cerr << "Actual output:\n" << output << "\n"; + } + + REQUIRE(output_matches); } TEST_CASE("show verification nosuchfile.o", "[netsh][verification]")