From f863a02469ceb45ef3b1bc9d9efe04540417d74d Mon Sep 17 00:00:00 2001 From: Dave Thaler Date: Tue, 7 Jun 2022 19:28:52 -0700 Subject: [PATCH] Create bpf_map structures from bpf_object__open() on a native file (#1160) * Create bpf_map structures from bpf_object__open() on a native file Fixes #1140 Signed-off-by: Dave Thaler * Fix map update when loading native object Signed-off-by: Dave Thaler * Update tests Signed-off-by: Dave Thaler * Fix for test Signed-off-by: Dave Thaler * More test fixes Signed-off-by: Dave Thaler * More test fixes Signed-off-by: Dave Thaler * PR feedback Signed-off-by: Dave Thaler * PR feedback Signed-off-by: Dave Thaler --- include/ebpf_api.h | 1 - libs/api/Verifier.cpp | 1 - libs/api/ebpf_api.cpp | 145 ++++++++++++++++++++++--------- libs/ebpfnetsh/elf.cpp | 43 +++++++-- libs/thunk/mock/mock.cpp | 2 +- tests/end_to_end/end_to_end.cpp | 77 ++++++++++++++++ tests/end_to_end/netsh_test.cpp | 78 ++++++++++++----- tests/end_to_end/test_helper.cpp | 7 +- tests/unit/libbpf_test.cpp | 42 +++++++++ 9 files changed, 322 insertions(+), 74 deletions(-) diff --git a/include/ebpf_api.h b/include/ebpf_api.h index 5037b59f5..0b106d6d2 100644 --- a/include/ebpf_api.h +++ b/include/ebpf_api.h @@ -102,7 +102,6 @@ extern "C" _Field_z_ const char* section_name; _Field_z_ const char* program_type_name; _Field_z_ const char* program_name; - size_t map_count; size_t raw_data_size; _Field_size_(raw_data_size) char* raw_data; ebpf_stat_t* stats; diff --git a/libs/api/Verifier.cpp b/libs/api/Verifier.cpp index f7384294e..56a096ace 100644 --- a/libs/api/Verifier.cpp +++ b/libs/api/Verifier.cpp @@ -404,7 +404,6 @@ ebpf_api_elf_enumerate_sections( info->section_name = _strdup(raw_program.section.c_str()); info->program_type_name = _strdup(raw_program.info.type.name.c_str()); - info->map_count = raw_program.info.map_descriptors.size(); std::vector raw_data = convert_ebpf_program_to_bytes(raw_program.prog); info->raw_data_size = raw_data.size(); diff --git a/libs/api/ebpf_api.cpp b/libs/api/ebpf_api.cpp index de998c4f2..baf4561e9 100644 --- a/libs/api/ebpf_api.cpp +++ b/libs/api/ebpf_api.cpp @@ -66,6 +66,13 @@ _ebpf_program_load_native( _Inout_ struct bpf_object* object, _Out_ fd_t* program_fd); +static const char* +_ebpf_get_section_string( + _In_ const struct _ebpf_pe_context* pe_context, + uintptr_t address, + _In_ const image_section_header& section_header, + _In_ const bounded_buffer* buffer); + static fd_t _create_file_descriptor_for_handle(ebpf_handle_t handle) noexcept { @@ -1422,16 +1429,15 @@ _initialize_ebpf_maps_native( goto Exit; } - map = (ebpf_map_t*)calloc(1, sizeof(ebpf_map_t)); - if (map == nullptr) { - result = EBPF_NO_MEMORY; - goto Exit; - } + map = maps[i]; + + // Note that the map name need not match, if the map was reused + // based on a pin path. Other fields ought to match however. + ebpf_assert(map->map_definition.type == info.type); + ebpf_assert(map->map_definition.key_size == info.key_size); + ebpf_assert(map->map_definition.value_size == info.value_size); + ebpf_assert(map->map_definition.max_entries == info.max_entries); - map->map_definition.type = info.type; - map->map_definition.key_size = info.key_size; - map->map_definition.value_size = info.value_size; - map->map_definition.max_entries = info.max_entries; map->map_definition.inner_map_id = info.inner_map_id; map->map_fd = _create_file_descriptor_for_handle(map_handles[i]); if (map->map_fd == ebpf_fd_invalid) { @@ -1440,14 +1446,6 @@ _initialize_ebpf_maps_native( } map->map_handle = map_handles[i]; map_handles[i] = ebpf_handle_invalid; - map->name = _strdup(info.name); - if (map->name == nullptr) { - result = EBPF_NO_MEMORY; - goto Exit; - } - - maps.emplace_back(map); - map = nullptr; } Exit: @@ -1531,7 +1529,6 @@ _initialize_ebpf_object_native( ebpf_assert(object.file_name != nullptr); ebpf_assert(object.object_name != nullptr); - // TODO(issue #1140): populate maps at open time for (auto& map : object.maps) { map->object = &object; } @@ -1547,6 +1544,8 @@ Exit: static ebpf_result_t _ebpf_enumerate_native_sections( _In_z_ const char* file, + _Inout_opt_ ebpf_object_t* object, + _In_opt_z_ const char* pin_root_path, _Outptr_result_maybenull_ ebpf_section_info_t** infos, _Outptr_result_maybenull_z_ const char** error_message); @@ -1554,7 +1553,7 @@ static ebpf_result_t _initialize_ebpf_object_from_native_file( _In_z_ const char* file_name, _In_opt_z_ const char* pin_root_path, - _Out_ ebpf_object_t& object, + _Inout_ ebpf_object_t& object, _Outptr_result_maybenull_z_ const char** error_message) noexcept { ebpf_program_t* program = nullptr; @@ -1564,7 +1563,7 @@ _initialize_ebpf_object_from_native_file( ebpf_assert(error_message); ebpf_section_info_t* infos = nullptr; - ebpf_result_t result = _ebpf_enumerate_native_sections(file_name, &infos, error_message); + ebpf_result_t result = _ebpf_enumerate_native_sections(file_name, &object, pin_root_path, &infos, error_message); if (result != EBPF_SUCCESS) { goto Exit; } @@ -1609,9 +1608,6 @@ _initialize_ebpf_object_from_native_file( program = nullptr; } - // TODO(issue #1140): populate object.maps - UNREFERENCED_PARAMETER(pin_root_path); - Exit: free(program); if (result != EBPF_SUCCESS) { @@ -1626,7 +1622,7 @@ static ebpf_result_t _initialize_ebpf_object_from_elf( _In_z_ const char* file_name, _In_opt_z_ const char* pin_root_path, - _Out_ ebpf_object_t& object, + _Inout_ ebpf_object_t& object, _Outptr_result_maybenull_z_ const char** error_message) noexcept { EBPF_LOG_ENTRY(); @@ -1784,12 +1780,14 @@ ebpf_free_sections(_In_opt_ ebpf_section_info_t* infos) typedef struct _ebpf_pe_context { + ebpf_result_t result; + ebpf_object_t* object; + const char* pin_root_path; uintptr_t image_base; ebpf_section_info_t* infos; std::map section_names; std::map program_names; std::map section_program_types; - int map_count; uintptr_t rdata_base; size_t rdata_size; const bounded_buffer* rdata_buffer; @@ -1799,7 +1797,7 @@ typedef struct _ebpf_pe_context } ebpf_pe_context_t; static int -_ebpf_pe_get_map_count( +_ebpf_pe_get_map_definitions( void* context, const VA& va, const std::string& section_name, @@ -1810,18 +1808,73 @@ _ebpf_pe_get_map_count( UNREFERENCED_PARAMETER(va); UNREFERENCED_PARAMETER(buffer); + ebpf_map_t* map = nullptr; ebpf_pe_context_t* pe_context = (ebpf_pe_context_t*)context; if (section_name == "maps") { // bpf2c generates a section that has map names as strings at the // start of the section. Skip over them looking for the map_entry_t - // which starts with a 16-byte-aligned NULL pointer. + // which starts with an 8-byte-aligned NULL pointer where the previous + // byte (if any) is also 00, and the following 8 bytes are non-NULL. uint32_t map_offset = 0; uint64_t zero = 0; - while (map_offset < section_header.Misc.VirtualSize && - memcmp(buffer->buf + map_offset, &zero, sizeof(zero)) != 0) { - map_offset += 16; + while (map_offset + 16 < section_header.Misc.VirtualSize && + (memcmp(buffer->buf + map_offset, &zero, sizeof(zero)) != 0 || + (map_offset > 0 && buffer->buf[map_offset - 1] != 0) || + memcmp(buffer->buf + map_offset + 8, &zero, sizeof(zero)) == 0)) { + map_offset += 8; + } + if (pe_context->object != nullptr) { + for (int map_index = 0; map_offset + sizeof(map_entry_t) <= section_header.Misc.VirtualSize; + map_offset += sizeof(map_entry_t), map_index++) { + map_entry_t* entry = (map_entry_t*)(buffer->buf + map_offset); + + map = (ebpf_map_t*)calloc(1, sizeof(ebpf_map_t)); + if (map == nullptr) { + goto Error; + } + + map->map_handle = ebpf_handle_invalid; + map->original_fd = (fd_t)map_index; + map->map_definition.type = entry->definition.type; + map->map_definition.key_size = entry->definition.key_size; + map->map_definition.value_size = entry->definition.value_size; + map->map_definition.max_entries = entry->definition.max_entries; + map->map_definition.pinning = entry->definition.pinning; + map->map_definition.inner_map_id = entry->definition.inner_id; + map->inner_map_original_fd = entry->definition.inner_map_idx; + map->pinned = false; + map->reused = false; + map->pin_path = nullptr; + + const char* map_name = + _ebpf_get_section_string(pe_context, (uintptr_t)entry->name, section_header, buffer); + map->name = _strdup(map_name); + if (map->name == nullptr) { + pe_context->result = EBPF_NO_MEMORY; + goto Error; + } + if (map->map_definition.pinning == PIN_GLOBAL_NS) { + char pin_path_buffer[EBPF_MAX_PIN_PATH_LENGTH]; + int len = snprintf( + pin_path_buffer, + EBPF_MAX_PIN_PATH_LENGTH, + "%s/%s", + pe_context->pin_root_path ? pe_context->pin_root_path : DEFAULT_PIN_ROOT_PATH, + map->name); + if (len < 0 || len >= EBPF_MAX_PIN_PATH_LENGTH) { + pe_context->result = EBPF_INVALID_ARGUMENT; + goto Error; + } + map->pin_path = _strdup(pin_path_buffer); + if (map->pin_path == nullptr) { + pe_context->result = EBPF_NO_MEMORY; + goto Error; + } + } + pe_context->object->maps.emplace_back(map); + map = nullptr; + } } - pe_context->map_count = (section_header.Misc.VirtualSize - map_offset) / sizeof(map_entry_t); } else if (section_name == ".rdata") { pe_context->rdata_base = pe_context->image_base + section_header.VirtualAddress; pe_context->rdata_size = section_header.Misc.VirtualSize; @@ -1832,11 +1885,18 @@ _ebpf_pe_get_map_count( pe_context->data_buffer = buffer; } - EBPF_LOG_EXIT(); + EBPF_LOG_FUNCTION_SUCCESS(); return 0; + +Error: + if (map) { + clean_up_ebpf_map(map); + } + EBPF_LOG_FUNCTION_ERROR(pe_context->result); + return 1; } -const char* +static const char* _ebpf_get_section_string( _In_ const ebpf_pe_context_t* pe_context, uintptr_t address, @@ -1876,7 +1936,7 @@ _ebpf_pe_get_section_names( // byte (if any) is also 00. uint32_t program_offset = 0; uint64_t zero = 0; - while (program_offset < section_header.Misc.VirtualSize && + while (program_offset + sizeof(zero) <= section_header.Misc.VirtualSize && (memcmp(buffer->buf + program_offset, &zero, sizeof(zero)) != 0 || (program_offset > 0 && buffer->buf[program_offset - 1] != 0))) { program_offset += 16; @@ -1946,7 +2006,6 @@ _ebpf_pe_add_section( _strdup(get_program_type_windows(pe_context->section_program_types[pe_section_name]).name.c_str()); info->raw_data_size = section_header.Misc.VirtualSize; info->raw_data = (char*)malloc(section_header.Misc.VirtualSize); - info->map_count = pe_context->map_count; if (info->raw_data == nullptr || info->program_type_name == nullptr || info->section_name == nullptr) { _ebpf_free_section_info(info); EBPF_LOG_EXIT(); @@ -1968,6 +2027,8 @@ _ebpf_pe_add_section( static ebpf_result_t _ebpf_enumerate_native_sections( _In_z_ const char* file, + _Inout_opt_ ebpf_object_t* object, + _In_opt_z_ const char* pin_root_path, _Outptr_result_maybenull_ ebpf_section_info_t** infos, _Outptr_result_maybenull_z_ const char** error_message) { @@ -1980,15 +2041,19 @@ _ebpf_enumerate_native_sections( EBPF_RETURN_RESULT(EBPF_FILE_NOT_FOUND); } - ebpf_pe_context_t context = {.image_base = pe->peHeader.nt.OptionalHeader64.ImageBase}; - IterSec(pe, _ebpf_pe_get_map_count, &context); + ebpf_pe_context_t context = { + .result = EBPF_SUCCESS, + .object = object, + .pin_root_path = pin_root_path, + .image_base = pe->peHeader.nt.OptionalHeader64.ImageBase}; + IterSec(pe, _ebpf_pe_get_map_definitions, &context); IterSec(pe, _ebpf_pe_get_section_names, &context); IterSec(pe, _ebpf_pe_add_section, &context); DestructParsedPE(pe); *infos = context.infos; - EBPF_RETURN_RESULT(EBPF_SUCCESS); + EBPF_RETURN_RESULT(context.result); } ebpf_result_t @@ -2003,7 +2068,7 @@ ebpf_enumerate_sections( std::string file_extension = file_name_string.substr(file_name_string.find_last_of(".") + 1); if (file_extension == "dll" || file_extension == "sys") { // Verbose is currently unused. - EBPF_RETURN_RESULT(_ebpf_enumerate_native_sections(file, infos, error_message)); + EBPF_RETURN_RESULT(_ebpf_enumerate_native_sections(file, nullptr, nullptr, infos, error_message)); } else { EBPF_RETURN_RESULT( ebpf_api_elf_enumerate_sections(file, nullptr, verbose, infos, error_message) ? EBPF_FAILED : EBPF_SUCCESS); @@ -2925,7 +2990,7 @@ ebpf_map_next(_In_opt_ const struct bpf_map* previous, _In_ const struct bpf_obj goto Exit; } if (previous == nullptr) { - map = object->maps[0]; + map = (object->maps.size() > 0) ? object->maps[0] : nullptr; } else { size_t maps_count = object->maps.size(); for (size_t i = 0; i < maps_count; i++) { diff --git a/libs/ebpfnetsh/elf.cpp b/libs/ebpfnetsh/elf.cpp index 6c9ddcb5b..1498fdf8b 100644 --- a/libs/ebpfnetsh/elf.cpp +++ b/libs/ebpfnetsh/elf.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "bpf/libbpf.h" #include "elf.h" #include "tokens.h" #include "utilities.h" @@ -69,6 +70,13 @@ handle_ebpf_show_disassembly( } } +static PCSTR +_get_map_type_name(ebpf_map_type_t type) +{ + int index = (type >= _countof(_ebpf_map_display_names)) ? 0 : type; + return _ebpf_map_display_names[index]; +} + DWORD handle_ebpf_show_sections( LPCWSTR machine, LPWSTR* argv, DWORD current_index, DWORD argc, DWORD flags, LPCVOID data, BOOL* done) @@ -126,7 +134,6 @@ handle_ebpf_show_sections( } ebpf_section_info_t* section_data = nullptr; - const char* error_message = nullptr; if (ebpf_enumerate_sections(filename.c_str(), level == VL_VERBOSE, §ion_data, &error_message) != 0) { std::cerr << error_message << std::endl; @@ -137,8 +144,9 @@ handle_ebpf_show_sections( if (level == VL_NORMAL) { std::cout << "\n"; - std::cout << " Section Type # Maps Size\n"; - std::cout << "==================== ========= ====== ======\n"; + std::cout << " Size\n"; + std::cout << " Section Type (bytes)\n"; + std::cout << "==================== ========= =======\n"; } for (auto current_section = section_data; current_section != nullptr; current_section = current_section->next) { if (!section.empty() && strcmp(current_section->section_name, section.c_str()) != 0) { @@ -146,22 +154,43 @@ handle_ebpf_show_sections( } if (level == VL_NORMAL) { std::cout << std::setw(20) << std::right << current_section->section_name << " " << std::setw(9) - << current_section->program_type_name << " " << std::setw(6) << current_section->map_count - << " " << std::setw(6) << current_section->raw_data_size << "\n"; + << current_section->program_type_name << " " << std::setw(7) << current_section->raw_data_size + << "\n"; } else { std::cout << "\n"; std::cout << "Section : " << current_section->section_name << "\n"; std::cout << "Program Type : " << current_section->program_type_name << "\n"; - std::cout << "# Maps : " << current_section->map_count << "\n"; std::cout << "Size : " << current_section->raw_data_size << " bytes\n"; for (auto stat = current_section->stats; stat != nullptr; stat = stat->next) { std::cout << std::setw(13) << std::left << stat->key << ": " << stat->value << "\n"; } } } - ebpf_free_sections(section_data); ebpf_free_string(error_message); + + // Show maps. + std::cout << "\n"; + std::cout << " Key Value Max\n"; + std::cout << " Map Type Size Size Entries Name\n"; + std::cout << "================== ==== ===== ======= ========\n"; + bpf_object* object = bpf_object__open(filename.c_str()); + if (object == nullptr) { + std::cout << "Couldn't get maps from " << filename << "\n"; + return ERROR_SUPPRESS_OUTPUT; + } + bpf_map* map; + bpf_object__for_each_map(map, object) + { + printf( + "%18s%6u%7u%9u %s\n", + _get_map_type_name(bpf_map__type(map)), + bpf_map__key_size(map), + bpf_map__value_size(map), + bpf_map__max_entries(map), + bpf_map__name(map)); + } + bpf_object__close(object); return NO_ERROR; } diff --git a/libs/thunk/mock/mock.cpp b/libs/thunk/mock/mock.cpp index 3d7a70b2b..c56edb7b3 100644 --- a/libs/thunk/mock/mock.cpp +++ b/libs/thunk/mock/mock.cpp @@ -115,7 +115,7 @@ _is_native_program(_In_z_ const char* file_name) { std::string file_name_string(file_name); std::string file_extension = file_name_string.substr(file_name_string.find_last_of(".") + 1); - if (file_extension == "dll") { + if (file_extension == "dll" || file_extension == "sys") { return true; } diff --git a/tests/end_to_end/end_to_end.cpp b/tests/end_to_end/end_to_end.cpp index 28b040579..4f41eb8bd 100644 --- a/tests/end_to_end/end_to_end.cpp +++ b/tests/end_to_end/end_to_end.cpp @@ -1713,6 +1713,12 @@ _map_reuse_test(ebpf_execution_type_t execution_type) error = bpf_obj_pin(outer_map_fd, "/ebpf/global/outer_map"); REQUIRE(error == 0); + // The outer map name created above should not have a name. + bpf_map_info info; + uint32_t info_size = sizeof(info); + REQUIRE(bpf_obj_get_info_by_fd(outer_map_fd, &info, &info_size) == 0); + REQUIRE(info.name[0] == 0); + int port_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(__u32), sizeof(__u32), 1, 0); REQUIRE(port_map_fd > 0); @@ -1730,6 +1736,11 @@ _map_reuse_test(ebpf_execution_type_t execution_type) program_load_attach_helper_t program_helper( file_name, BPF_PROG_TYPE_XDP, "lookup_update", EBPF_EXECUTION_ANY, &ifindex, sizeof(ifindex), hook); + // The outer map we created earlier should still not have a name even though there is a name in the file, + // since the unnamed map was reused. + REQUIRE(bpf_obj_get_info_by_fd(outer_map_fd, &info, &info_size) == 0); + REQUIRE(info.name[0] == 0); + auto packet = prepare_udp_packet(10, ETHERNET_TYPE_IPV4); xdp_md_t ctx{packet.data(), packet.data() + packet.size(), 0, TEST_IFINDEX}; int hook_result; @@ -1754,6 +1765,53 @@ _map_reuse_test(ebpf_execution_type_t execution_type) DECLARE_JIT_TEST_CASES("map_reuse", "[end_to_end]", _map_reuse_test); +// Try to reuse a map of the wrong type. +static void +_wrong_map_reuse_test(ebpf_execution_type_t execution_type) +{ + _test_helper_end_to_end test_helper; + single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP); + program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP); + + const char* file_name = (execution_type == EBPF_EXECUTION_NATIVE ? "map_reuse_um.dll" : "map_reuse.o"); + + // First create and pin the maps manually. + int inner_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(__u32), sizeof(__u32), 1, 0); + REQUIRE(inner_map_fd > 0); + + int outer_map_fd = bpf_create_map_in_map(BPF_MAP_TYPE_HASH_OF_MAPS, nullptr, sizeof(__u32), inner_map_fd, 1, 0); + REQUIRE(outer_map_fd > 0); + + // Pin the outer map and port map to the wrong paths so they won't match what is in the ebpf program. + REQUIRE(bpf_obj_pin(outer_map_fd, "/ebpf/global/port_map") == 0); + + int port_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(__u32), sizeof(__u32), 1, 0); + REQUIRE(port_map_fd > 0); + + // Pin port map. + REQUIRE(bpf_obj_pin(port_map_fd, "/ebpf/global/outer_map") == 0); + + // Open eBPF program file. + bpf_object* object = bpf_object__open(file_name); + REQUIRE(object != nullptr); + bpf_program* program = bpf_object__next_program(object, nullptr); + REQUIRE(program != nullptr); + + // Try to load the program. This should fail because the maps can't be reused. + REQUIRE(bpf_object__load(object) == -EINVAL); + + bpf_object__close(object); + + Platform::_close(outer_map_fd); + Platform::_close(inner_map_fd); + Platform::_close(port_map_fd); + + REQUIRE(ebpf_object_unpin("/ebpf/global/outer_map") == EBPF_SUCCESS); + REQUIRE(ebpf_object_unpin("/ebpf/global/port_map") == EBPF_SUCCESS); +} + +DECLARE_JIT_TEST_CASES("wrong_map_reuse", "[end_to_end]", _wrong_map_reuse_test); + static void _auto_pinned_maps_test(ebpf_execution_type_t execution_type) { @@ -2213,6 +2271,25 @@ TEST_CASE("load_native_program_negative4", "[end-to-end]") Platform::_delete_service(service_handle); } +// Try to load a .sys in user mode. +TEST_CASE("load_native_program_negative5", "[end_to_end]") +{ + _test_helper_end_to_end test_helper; + + int result; + const char* error_message = nullptr; + bpf_object* object = nullptr; + fd_t program_fd; + + single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP); + program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP); + + set_native_module_failures(true); + result = + ebpf_program_load("map.sys", BPF_PROG_TYPE_UNSPEC, EBPF_EXECUTION_ANY, &object, &program_fd, &error_message); + REQUIRE(result == -ENOENT); +} + // The below tests try to load native drivers for invalid programs (that will fail verification). // Since verification can be skipped in bpf2c for only Debug builds, these tests are applicable // only for Debug build. diff --git a/tests/end_to_end/netsh_test.cpp b/tests/end_to_end/netsh_test.cpp index 1cf74d45f..02ecb0aa3 100644 --- a/tests/end_to_end/netsh_test.cpp +++ b/tests/end_to_end/netsh_test.cpp @@ -94,9 +94,14 @@ TEST_CASE("show sections bpf.o", "[netsh][sections]") REQUIRE(result == NO_ERROR); REQUIRE( output == "\n" - " Section Type # Maps Size\n" - "==================== ========= ====== ======\n" - " .text xdp 0 16\n"); + " Size\n" + " Section Type (bytes)\n" + "==================== ========= =======\n" + " .text xdp 16\n" + "\n" + " Key Value Max\n" + " Map Type Size Size Entries Name\n" + "================== ==== ===== ======= ========\n"); } // Test specifying a section name. @@ -109,7 +114,6 @@ TEST_CASE("show sections bpf.o .text", "[netsh][sections]") output == "\n" "Section : .text\n" "Program Type : xdp\n" - "# Maps : 0\n" "Size : 16 bytes\n" "Instructions : 2\n" "adjust_head : 0\n" @@ -128,7 +132,11 @@ TEST_CASE("show sections bpf.o .text", "[netsh][sections]") "map_in_map : 0\n" "other : 2\n" "packet_access: 0\n" - "store : 0\n"); + "store : 0\n" + "\n" + " Key Value Max\n" + " Map Type Size Size Entries Name\n" + "================== ==== ===== ======= ========\n"); } // Test a .sys file. @@ -140,9 +148,14 @@ TEST_CASE("show sections bpf.sys", "[netsh][sections]") REQUIRE( output == "\n" - " Section Type # Maps Size\n" - "==================== ========= ====== ======\n" - " .text xdp 0 1752\n"); + " Size\n" + " Section Type (bytes)\n" + "==================== ========= =======\n" + " .text xdp 1752\n" + "\n" + " Key Value Max\n" + " Map Type Size Size Entries Name\n" + "================== ==== ===== ======= ========\n"); } // Test a DLL with multiple maps in the map section. @@ -153,9 +166,17 @@ TEST_CASE("show sections map_reuse_um.dll", "[netsh][sections]") REQUIRE(result == NO_ERROR); REQUIRE( output == "\n" - " Section Type # Maps Size\n" - "==================== ========= ====== ======\n" - " xdp_prog xdp 3 1087\n"); + " Size\n" + " Section Type (bytes)\n" + "==================== ========= =======\n" + " xdp_prog xdp 1087\n" + "\n" + " Key Value Max\n" + " Map Type Size Size Entries Name\n" + "================== ==== ===== ======= ========\n" + " Hash of maps 4 4 1 outer_map\n" + " Array 4 4 1 inner_map\n" + " Array 4 4 1 port_map\n"); } // Test a .dll file with multiple programs. @@ -167,11 +188,17 @@ TEST_CASE("show sections tail_call_multiple_um.dll", "[netsh][sections]") REQUIRE(result == NO_ERROR); REQUIRE( output == "\n" - " Section Type # Maps Size\n" - "==================== ========= ====== ======\n" - " xdp_prog xdp 0 413\n" - " xdp_prog/0 xdp 0 413\n" - " xdp_prog/1 xdp 0 190\n"); + " Size\n" + " Section Type (bytes)\n" + "==================== ========= =======\n" + " xdp_prog xdp 413\n" + " xdp_prog/0 xdp 413\n" + " xdp_prog/1 xdp 190\n" + "\n" + " Key Value Max\n" + " Map Type Size Size Entries Name\n" + "================== ==== ===== ======= ========\n" + " Program array 4 4 10 map\n"); } // Test a .sys file with multiple programs, including ones with long names. @@ -183,12 +210,19 @@ TEST_CASE("show sections cgroup_sock_addr.sys", "[netsh][sections]") REQUIRE(result == NO_ERROR); REQUIRE( output == "\n" - " Section Type # Maps Size\n" - "==================== ========= ====== ======\n" - " cgroup/connect4 sock_addr 2 594\n" - " cgroup/connect6 sock_addr 2 728\n" - " cgroup/recv_accept4 sock_addr 2 594\n" - " cgroup/recv_accept6 sock_addr 2 728\n"); + " Size\n" + " Section Type (bytes)\n" + "==================== ========= =======\n" + " cgroup/connect4 sock_addr 594\n" + " cgroup/connect6 sock_addr 728\n" + " cgroup/recv_accept4 sock_addr 594\n" + " cgroup/recv_accept6 sock_addr 728\n" + "\n" + " Key Value Max\n" + " Map Type Size Size Entries Name\n" + "================== ==== ===== ======= ========\n" + " Hash 44 4 1 ingress_connection_policy_map\n" + " Hash 44 4 1 egress_connection_policy_map\n"); } TEST_CASE("show verification nosuchfile.o", "[netsh][verification]") diff --git a/tests/end_to_end/test_helper.cpp b/tests/end_to_end/test_helper.cpp index 59797b0ac..024573384 100644 --- a/tests/end_to_end/test_helper.cpp +++ b/tests/end_to_end/test_helper.cpp @@ -257,7 +257,10 @@ _preprocess_load_native_module(_Inout_ service_context_t* context) auto get_function = reinterpret_cast(GetProcAddress(context->dll, "get_metadata_table")); - REQUIRE(get_function != nullptr); + if (get_function == nullptr) { + REQUIRE(_expect_native_module_load_failures); + return; + } metadata_table_t* table = get_function(); REQUIRE(table != nullptr); @@ -479,8 +482,8 @@ Glue_delete_service(SC_HANDLE handle) // Delete the service if it has not been loaded yet. Otherwise // mark it pending for delete. if (!context->loaded) { - _service_path_to_context_map.erase(path); ebpf_free(context); + _service_path_to_context_map.erase(path); } else { context->delete_pending = true; } diff --git a/tests/unit/libbpf_test.cpp b/tests/unit/libbpf_test.cpp index fd51a9e69..6e4ae6e0d 100644 --- a/tests/unit/libbpf_test.cpp +++ b/tests/unit/libbpf_test.cpp @@ -1981,6 +1981,16 @@ TEST_CASE("bpf_object__open_file with .dll", "[libbpf]") REQUIRE(bpf_object__next_program(object, program) == nullptr); + struct bpf_map* map = bpf_object__next_map(object, nullptr); + REQUIRE(map != nullptr); + REQUIRE(strcmp(bpf_map__name(map), "dropped_packet_map") == 0); + REQUIRE(bpf_map__fd(map) == ebpf_fd_invalid); + map = bpf_object__next_map(object, map); + REQUIRE(strcmp(bpf_map__name(map), "interface_index_map") == 0); + REQUIRE(bpf_map__fd(map) == ebpf_fd_invalid); + map = bpf_object__next_map(object, map); + REQUIRE(map == nullptr); + // Trying to attach the program should fail since it's not loaded yet. bpf_link* link = bpf_program__attach(program); REQUIRE(link == nullptr); @@ -1989,6 +1999,17 @@ TEST_CASE("bpf_object__open_file with .dll", "[libbpf]") // Load the program. REQUIRE(bpf_object__load(object) == 0); + // The maps should now have FDs. + map = bpf_object__next_map(object, nullptr); + REQUIRE(map != nullptr); + REQUIRE(strcmp(bpf_map__name(map), "dropped_packet_map") == 0); + REQUIRE(bpf_map__fd(map) != ebpf_fd_invalid); + map = bpf_object__next_map(object, map); + REQUIRE(strcmp(bpf_map__name(map), "interface_index_map") == 0); + REQUIRE(bpf_map__fd(map) != ebpf_fd_invalid); + map = bpf_object__next_map(object, map); + REQUIRE(map == nullptr); + // Attach should now succeed. link = bpf_program__attach(program); REQUIRE(link != nullptr); @@ -2022,6 +2043,16 @@ TEST_CASE("bpf_object__load with .o", "[libbpf]") REQUIRE(bpf_program__set_type(program, BPF_PROG_TYPE_XDP) == 0); + struct bpf_map* map = bpf_object__next_map(object, nullptr); + REQUIRE(map != nullptr); + REQUIRE(strcmp(bpf_map__name(map), "dropped_packet_map") == 0); + REQUIRE(bpf_map__fd(map) == ebpf_fd_invalid); + map = bpf_object__next_map(object, map); + REQUIRE(strcmp(bpf_map__name(map), "interface_index_map") == 0); + REQUIRE(bpf_map__fd(map) == ebpf_fd_invalid); + map = bpf_object__next_map(object, map); + REQUIRE(map == nullptr); + // Trying to attach the program should fail since it's not loaded yet. bpf_link* link = bpf_program__attach(program); REQUIRE(link == nullptr); @@ -2034,6 +2065,17 @@ TEST_CASE("bpf_object__load with .o", "[libbpf]") link = bpf_program__attach(program); REQUIRE(link != nullptr); + // The maps should now have FDs. + map = bpf_object__next_map(object, nullptr); + REQUIRE(map != nullptr); + REQUIRE(strcmp(bpf_map__name(map), "dropped_packet_map") == 0); + REQUIRE(bpf_map__fd(map) != ebpf_fd_invalid); + map = bpf_object__next_map(object, map); + REQUIRE(strcmp(bpf_map__name(map), "interface_index_map") == 0); + REQUIRE(bpf_map__fd(map) != ebpf_fd_invalid); + map = bpf_object__next_map(object, map); + REQUIRE(map == nullptr); + REQUIRE(bpf_link__destroy(link) == 0); bpf_object__close(object); }