679 строки
25 KiB
C++
679 строки
25 KiB
C++
// Copyright (c) Microsoft Corporation
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Windows build system requires include of Windows.h before other Windows
|
|
// headers.
|
|
#include <Windows.h>
|
|
|
|
#include <chrono>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <sddl.h>
|
|
|
|
#include "catch_wrapper.hpp"
|
|
#include "ebpf_bind_program_data.h"
|
|
#include "ebpf_epoch.h"
|
|
#include "ebpf_nethooks.h"
|
|
#include "ebpf_platform.h"
|
|
#include "ebpf_pinning_table.h"
|
|
#include "ebpf_program_types.h"
|
|
#include "ebpf_serialize.h"
|
|
#include "ebpf_xdp_program_data.h"
|
|
#include "ebpf_state.h"
|
|
|
|
class _test_helper
|
|
{
|
|
public:
|
|
_test_helper()
|
|
{
|
|
ebpf_object_tracking_initiate();
|
|
REQUIRE(ebpf_platform_initiate() == EBPF_SUCCESS);
|
|
platform_initiated = true;
|
|
REQUIRE(ebpf_epoch_initiate() == EBPF_SUCCESS);
|
|
epoch_initated = true;
|
|
}
|
|
~_test_helper()
|
|
{
|
|
if (epoch_initated)
|
|
ebpf_epoch_terminate();
|
|
if (platform_initiated)
|
|
ebpf_platform_terminate();
|
|
ebpf_object_tracking_terminate();
|
|
}
|
|
|
|
private:
|
|
bool platform_initiated = false;
|
|
bool epoch_initated = false;
|
|
};
|
|
|
|
TEST_CASE("hash_table_test", "[platform]")
|
|
{
|
|
ebpf_hash_table_t* table = nullptr;
|
|
std::vector<uint8_t> key_1(13);
|
|
std::vector<uint8_t> key_2(13);
|
|
std::vector<uint8_t> data_1(37);
|
|
std::vector<uint8_t> data_2(37);
|
|
uint8_t* returned_value = nullptr;
|
|
std::vector<uint8_t> returned_key(13);
|
|
|
|
for (auto& v : key_1) {
|
|
v = static_cast<uint8_t>(ebpf_random_uint32());
|
|
}
|
|
for (auto& v : key_2) {
|
|
v = static_cast<uint8_t>(ebpf_random_uint32());
|
|
}
|
|
for (auto& v : data_1) {
|
|
v = static_cast<uint8_t>(ebpf_random_uint32());
|
|
}
|
|
for (auto& v : data_2) {
|
|
v = static_cast<uint8_t>(ebpf_random_uint32());
|
|
}
|
|
|
|
REQUIRE(
|
|
ebpf_hash_table_create(&table, ebpf_allocate, ebpf_free, key_1.size(), data_1.size(), 1, NULL) == EBPF_SUCCESS);
|
|
|
|
// Insert first
|
|
REQUIRE(
|
|
ebpf_hash_table_update(table, key_1.data(), data_1.data(), nullptr, EBPF_HASH_TABLE_OPERATION_INSERT) ==
|
|
EBPF_SUCCESS);
|
|
|
|
// Insert second
|
|
REQUIRE(
|
|
ebpf_hash_table_update(table, key_2.data(), data_2.data(), nullptr, EBPF_HASH_TABLE_OPERATION_ANY) ==
|
|
EBPF_SUCCESS);
|
|
|
|
// Find the first
|
|
REQUIRE(ebpf_hash_table_find(table, key_1.data(), &returned_value) == EBPF_SUCCESS);
|
|
REQUIRE(memcmp(returned_value, data_1.data(), data_1.size()) == 0);
|
|
|
|
// Find the second
|
|
REQUIRE(ebpf_hash_table_find(table, key_2.data(), &returned_value) == EBPF_SUCCESS);
|
|
REQUIRE(memcmp(returned_value, data_2.data(), data_2.size()) == 0);
|
|
|
|
// Replace
|
|
memset(data_1.data(), '0x55', data_1.size());
|
|
REQUIRE(
|
|
ebpf_hash_table_update(table, key_1.data(), data_1.data(), nullptr, EBPF_HASH_TABLE_OPERATION_REPLACE) ==
|
|
EBPF_SUCCESS);
|
|
|
|
// Find the first
|
|
REQUIRE(ebpf_hash_table_find(table, key_1.data(), &returned_value) == EBPF_SUCCESS);
|
|
REQUIRE(memcmp(returned_value, data_1.data(), data_1.size()) == 0);
|
|
|
|
// Next key
|
|
REQUIRE(ebpf_hash_table_next_key(table, nullptr, returned_key.data()) == EBPF_SUCCESS);
|
|
REQUIRE((returned_key == key_1 || returned_key == key_2));
|
|
|
|
REQUIRE(ebpf_hash_table_next_key(table, returned_key.data(), returned_key.data()) == EBPF_SUCCESS);
|
|
REQUIRE((returned_key == key_1 || returned_key == key_2));
|
|
|
|
REQUIRE(ebpf_hash_table_next_key(table, returned_key.data(), returned_key.data()) == EBPF_NO_MORE_KEYS);
|
|
REQUIRE((returned_key == key_1 || returned_key == key_2));
|
|
|
|
// Delete found
|
|
REQUIRE(ebpf_hash_table_delete(table, key_1.data()) == EBPF_SUCCESS);
|
|
|
|
// Delete not found
|
|
REQUIRE(ebpf_hash_table_delete(table, key_1.data()) == EBPF_KEY_NOT_FOUND);
|
|
|
|
// Find not found
|
|
REQUIRE(ebpf_hash_table_find(table, key_1.data(), &returned_value) == EBPF_KEY_NOT_FOUND);
|
|
|
|
ebpf_hash_table_destroy(table);
|
|
}
|
|
|
|
void
|
|
run_in_epoch(std::function<void()> function)
|
|
{
|
|
if (ebpf_epoch_enter() == EBPF_SUCCESS) {
|
|
function();
|
|
ebpf_epoch_exit();
|
|
}
|
|
}
|
|
|
|
TEST_CASE("hash_table_stress_test", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
ebpf_hash_table_t* table = nullptr;
|
|
const size_t iterations = 1000;
|
|
uint32_t worker_threads = ebpf_get_cpu_count();
|
|
uint32_t key_count = 4;
|
|
uint32_t load_factor = 4;
|
|
int32_t cpu_id = 0;
|
|
REQUIRE(
|
|
ebpf_hash_table_create(
|
|
&table,
|
|
ebpf_epoch_allocate,
|
|
ebpf_epoch_free,
|
|
sizeof(uint32_t),
|
|
sizeof(uint64_t),
|
|
static_cast<size_t>(worker_threads) * static_cast<size_t>(key_count),
|
|
NULL) == EBPF_SUCCESS);
|
|
auto worker = [table, iterations, key_count, load_factor, &cpu_id]() {
|
|
uint32_t next_key = 0;
|
|
uint64_t value = 11;
|
|
uint64_t** returned_value = nullptr;
|
|
std::vector<uint32_t> keys(static_cast<size_t>(key_count) * static_cast<size_t>(load_factor));
|
|
|
|
uint32_t local_cpu_id = ebpf_interlocked_increment_int32(&cpu_id) - 1;
|
|
uintptr_t thread_mask = local_cpu_id;
|
|
thread_mask = static_cast<uintptr_t>(1) << thread_mask;
|
|
SetThreadAffinityMask(GetCurrentThread(), thread_mask);
|
|
|
|
for (auto& key : keys) {
|
|
key = ebpf_random_uint32();
|
|
}
|
|
for (size_t i = 0; i < iterations; i++) {
|
|
for (auto& key : keys) {
|
|
run_in_epoch([&]() {
|
|
ebpf_hash_table_update(
|
|
table,
|
|
reinterpret_cast<const uint8_t*>(&key),
|
|
reinterpret_cast<const uint8_t*>(&value),
|
|
nullptr,
|
|
EBPF_HASH_TABLE_OPERATION_ANY);
|
|
});
|
|
}
|
|
for (auto& key : keys)
|
|
run_in_epoch([&]() {
|
|
ebpf_hash_table_find(
|
|
table, reinterpret_cast<const uint8_t*>(&key), reinterpret_cast<uint8_t**>(&returned_value));
|
|
});
|
|
for (auto& key : keys)
|
|
run_in_epoch([&]() {
|
|
ebpf_hash_table_next_key(
|
|
table, reinterpret_cast<const uint8_t*>(&key), reinterpret_cast<uint8_t*>(&next_key));
|
|
});
|
|
|
|
for (auto& key : keys)
|
|
run_in_epoch([&]() { ebpf_hash_table_delete(table, reinterpret_cast<const uint8_t*>(&key)); });
|
|
}
|
|
};
|
|
|
|
std::vector<std::thread> threads;
|
|
for (size_t i = 0; i < worker_threads; i++) {
|
|
threads.emplace_back(std::thread(worker));
|
|
}
|
|
|
|
for (auto& thread : threads) {
|
|
thread.join();
|
|
}
|
|
|
|
ebpf_hash_table_destroy(table);
|
|
}
|
|
|
|
TEST_CASE("pinning_test", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
typedef struct _some_object
|
|
{
|
|
ebpf_object_t object{};
|
|
std::string name;
|
|
} some_object_t;
|
|
|
|
some_object_t an_object;
|
|
some_object_t another_object;
|
|
some_object_t* some_object = nullptr;
|
|
ebpf_utf8_string_t foo = EBPF_UTF8_STRING_FROM_CONST_STRING("foo");
|
|
ebpf_utf8_string_t bar = EBPF_UTF8_STRING_FROM_CONST_STRING("bar");
|
|
|
|
ebpf_object_initialize(
|
|
&an_object.object, EBPF_OBJECT_MAP, [](ebpf_object_t*) {}, NULL);
|
|
ebpf_object_initialize(
|
|
&another_object.object, EBPF_OBJECT_MAP, [](ebpf_object_t*) {}, NULL);
|
|
|
|
ebpf_pinning_table_t* pinning_table = nullptr;
|
|
REQUIRE(ebpf_pinning_table_allocate(&pinning_table) == EBPF_SUCCESS);
|
|
|
|
REQUIRE(ebpf_pinning_table_insert(pinning_table, &foo, &an_object.object) == EBPF_SUCCESS);
|
|
REQUIRE(an_object.object.reference_count == 2);
|
|
REQUIRE(ebpf_pinning_table_insert(pinning_table, &bar, &another_object.object) == EBPF_SUCCESS);
|
|
REQUIRE(another_object.object.reference_count == 2);
|
|
REQUIRE(ebpf_pinning_table_find(pinning_table, &foo, (ebpf_object_t**)&some_object) == EBPF_SUCCESS);
|
|
REQUIRE(an_object.object.reference_count == 3);
|
|
REQUIRE(some_object == &an_object);
|
|
ebpf_object_release_reference(&some_object->object);
|
|
REQUIRE(ebpf_pinning_table_delete(pinning_table, &foo) == EBPF_SUCCESS);
|
|
REQUIRE(another_object.object.reference_count == 2);
|
|
|
|
ebpf_pinning_table_free(pinning_table);
|
|
REQUIRE(an_object.object.reference_count == 1);
|
|
REQUIRE(another_object.object.reference_count == 1);
|
|
|
|
ebpf_object_release_reference(&an_object.object);
|
|
ebpf_object_release_reference(&another_object.object);
|
|
}
|
|
|
|
TEST_CASE("epoch_test_single_epoch", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
REQUIRE(ebpf_epoch_enter() == EBPF_SUCCESS);
|
|
void* memory = ebpf_epoch_allocate(10);
|
|
ebpf_epoch_free(memory);
|
|
ebpf_epoch_exit();
|
|
ebpf_epoch_flush();
|
|
}
|
|
|
|
TEST_CASE("epoch_test_two_threads", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
auto epoch = []() {
|
|
ebpf_epoch_enter();
|
|
void* memory = ebpf_epoch_allocate(10);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
ebpf_epoch_free(memory);
|
|
ebpf_epoch_exit();
|
|
ebpf_epoch_flush();
|
|
};
|
|
|
|
std::thread thread_1(epoch);
|
|
std::thread thread_2(epoch);
|
|
thread_1.join();
|
|
thread_2.join();
|
|
}
|
|
|
|
TEST_CASE("extension_test", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
auto client_function = []() { return EBPF_SUCCESS; };
|
|
auto provider_function = []() { return EBPF_SUCCESS; };
|
|
auto provider_attach = [](void* context,
|
|
const GUID* client_id,
|
|
void* client_binding_context,
|
|
const ebpf_extension_data_t* client_data,
|
|
const ebpf_extension_dispatch_table_t* client_dispatch_table) {
|
|
UNREFERENCED_PARAMETER(context);
|
|
UNREFERENCED_PARAMETER(client_id);
|
|
UNREFERENCED_PARAMETER(client_data);
|
|
UNREFERENCED_PARAMETER(client_dispatch_table);
|
|
UNREFERENCED_PARAMETER(client_binding_context);
|
|
return EBPF_SUCCESS;
|
|
};
|
|
auto provider_detach = [](void* context, const GUID* client_id) {
|
|
UNREFERENCED_PARAMETER(context);
|
|
UNREFERENCED_PARAMETER(client_id);
|
|
return EBPF_SUCCESS;
|
|
};
|
|
ebpf_extension_dispatch_table_t client_dispatch_table = {
|
|
0, sizeof(ebpf_extension_dispatch_table_t), client_function};
|
|
ebpf_extension_dispatch_table_t provider_dispatch_table = {
|
|
0, sizeof(ebpf_extension_dispatch_table_t), provider_function};
|
|
ebpf_extension_data_t client_data{};
|
|
ebpf_extension_data_t provider_data{};
|
|
GUID interface_id;
|
|
|
|
const ebpf_extension_dispatch_table_t* returned_provider_dispatch_table;
|
|
const ebpf_extension_data_t* returned_provider_data;
|
|
|
|
ebpf_extension_provider_t* provider_context = nullptr;
|
|
ebpf_extension_client_t* client_context = nullptr;
|
|
void* provider_binding_context = nullptr;
|
|
|
|
ebpf_guid_create(&interface_id);
|
|
int callback_context = 0;
|
|
int client_binding_context = 0;
|
|
REQUIRE(
|
|
ebpf_provider_load(
|
|
&provider_context,
|
|
&interface_id,
|
|
nullptr,
|
|
&provider_data,
|
|
&provider_dispatch_table,
|
|
&callback_context,
|
|
provider_attach,
|
|
provider_detach) == EBPF_SUCCESS);
|
|
|
|
REQUIRE(
|
|
ebpf_extension_load(
|
|
&client_context,
|
|
&interface_id,
|
|
&client_binding_context,
|
|
&client_data,
|
|
&client_dispatch_table,
|
|
&provider_binding_context,
|
|
&returned_provider_data,
|
|
&returned_provider_dispatch_table,
|
|
nullptr) == EBPF_SUCCESS);
|
|
|
|
REQUIRE(returned_provider_data == &provider_data);
|
|
REQUIRE(returned_provider_dispatch_table == &provider_dispatch_table);
|
|
|
|
ebpf_extension_unload(client_context);
|
|
ebpf_provider_unload(provider_context);
|
|
}
|
|
|
|
TEST_CASE("trampoline_test", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
ebpf_trampoline_table_t* table = NULL;
|
|
ebpf_result_t (*test_function)();
|
|
auto provider_function1 = []() { return EBPF_SUCCESS; };
|
|
ebpf_result_t (*function_pointer1)() = provider_function1;
|
|
const void* helper_functions1[] = {(void*)function_pointer1};
|
|
const uint32_t provider_helper_function_ids[] = {(uint32_t)(EBPF_MAX_GENERAL_HELPER_FUNCTION + 1)};
|
|
ebpf_helper_function_addresses_t helper_function_addresses1 = {
|
|
EBPF_COUNT_OF(helper_functions1), (uint64_t*)helper_functions1};
|
|
|
|
auto provider_function2 = []() { return EBPF_OBJECT_ALREADY_EXISTS; };
|
|
ebpf_result_t (*function_pointer2)() = provider_function2;
|
|
const void* helper_functions2[] = {(void*)function_pointer2};
|
|
ebpf_helper_function_addresses_t helper_function_addresses2 = {
|
|
EBPF_COUNT_OF(helper_functions1), (uint64_t*)helper_functions2};
|
|
|
|
REQUIRE(ebpf_allocate_trampoline_table(1, &table) == EBPF_SUCCESS);
|
|
REQUIRE(
|
|
ebpf_update_trampoline_table(
|
|
table,
|
|
EBPF_COUNT_OF(provider_helper_function_ids),
|
|
provider_helper_function_ids,
|
|
&helper_function_addresses1) == EBPF_SUCCESS);
|
|
REQUIRE(ebpf_get_trampoline_function(table, 0, reinterpret_cast<void**>(&test_function)) == EBPF_SUCCESS);
|
|
|
|
// Verify that the trampoline function invokes the provider function
|
|
REQUIRE(test_function() == EBPF_SUCCESS);
|
|
|
|
REQUIRE(
|
|
ebpf_update_trampoline_table(
|
|
table,
|
|
EBPF_COUNT_OF(provider_helper_function_ids),
|
|
provider_helper_function_ids,
|
|
&helper_function_addresses2) == EBPF_SUCCESS);
|
|
|
|
// Verify that the trampoline function now invokes the new provider function
|
|
REQUIRE(test_function() == EBPF_OBJECT_ALREADY_EXISTS);
|
|
ebpf_free_trampoline_table(table);
|
|
}
|
|
|
|
static ebpf_helper_function_prototype_t _helper_functions[] = {
|
|
{1,
|
|
"bpf_map_lookup_elem",
|
|
EBPF_RETURN_TYPE_PTR_TO_MAP_VALUE_OR_NULL,
|
|
{EBPF_ARGUMENT_TYPE_PTR_TO_MAP, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_KEY}},
|
|
{2,
|
|
"bpf_map_update_elem",
|
|
EBPF_RETURN_TYPE_INTEGER,
|
|
{EBPF_ARGUMENT_TYPE_PTR_TO_MAP, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_KEY, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_VALUE}},
|
|
{3,
|
|
"bpf_map_delete_elem",
|
|
EBPF_RETURN_TYPE_PTR_TO_MAP_VALUE_OR_NULL,
|
|
{EBPF_ARGUMENT_TYPE_PTR_TO_MAP, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_KEY}},
|
|
{4,
|
|
"bpf_tail_call",
|
|
EBPF_RETURN_TYPE_INTEGER_OR_NO_RETURN_IF_SUCCEED,
|
|
{EBPF_ARGUMENT_TYPE_PTR_TO_CTX, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_OF_PROGRAMS, EBPF_ARGUMENT_TYPE_ANYTHING}},
|
|
{5, "bpf_get_prandom_u32", EBPF_RETURN_TYPE_INTEGER, {0}},
|
|
{6, "bpf_ktime_get_boot_ns", EBPF_RETURN_TYPE_INTEGER, {0}},
|
|
{7, "bpf_get_smp_processor_id", EBPF_RETURN_TYPE_INTEGER, {0}},
|
|
};
|
|
|
|
TEST_CASE("program_type_info", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
ebpf_context_descriptor_t context_descriptor{
|
|
sizeof(xdp_md_t),
|
|
EBPF_OFFSET_OF(xdp_md_t, data),
|
|
EBPF_OFFSET_OF(xdp_md_t, data_end),
|
|
EBPF_OFFSET_OF(xdp_md_t, data_meta)};
|
|
ebpf_program_type_descriptor_t program_type{"xdp", &context_descriptor};
|
|
ebpf_program_info_t program_info{program_type, _countof(_helper_functions), _helper_functions};
|
|
ebpf_program_info_t* new_program_info = nullptr;
|
|
uint8_t* buffer = nullptr;
|
|
unsigned long buffer_size;
|
|
REQUIRE(ebpf_program_info_encode(&program_info, &buffer, &buffer_size) == EBPF_SUCCESS);
|
|
REQUIRE(ebpf_program_info_decode(&new_program_info, buffer, buffer_size) == EBPF_SUCCESS);
|
|
ebpf_free(new_program_info);
|
|
}
|
|
|
|
TEST_CASE("program_type_info_stored", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
ebpf_program_info_t* xdp_program_info = nullptr;
|
|
ebpf_program_info_t* bind_program_info = nullptr;
|
|
REQUIRE(
|
|
ebpf_program_info_decode(
|
|
&xdp_program_info, _ebpf_encoded_xdp_program_info_data, sizeof(_ebpf_encoded_xdp_program_info_data)) ==
|
|
EBPF_SUCCESS);
|
|
REQUIRE(xdp_program_info->count_of_helpers == _countof(_helper_functions));
|
|
REQUIRE(strcmp(xdp_program_info->program_type_descriptor.name, "xdp") == 0);
|
|
ebpf_free(xdp_program_info);
|
|
|
|
REQUIRE(
|
|
ebpf_program_info_decode(
|
|
&bind_program_info, _ebpf_encoded_bind_program_info_data, sizeof(_ebpf_encoded_bind_program_info_data)) ==
|
|
EBPF_SUCCESS);
|
|
REQUIRE(strcmp(bind_program_info->program_type_descriptor.name, "bind") == 0);
|
|
REQUIRE(bind_program_info->count_of_helpers == _countof(_helper_functions));
|
|
ebpf_free(bind_program_info);
|
|
}
|
|
|
|
struct ebpf_security_descriptor_t_free
|
|
{
|
|
void
|
|
operator()(_Frees_ptr_opt_ ebpf_security_descriptor_t* p)
|
|
{
|
|
LocalFree(p);
|
|
}
|
|
};
|
|
typedef std::unique_ptr<ebpf_security_descriptor_t, ebpf_security_descriptor_t_free> ebpf_security_generic_mapping_ptr;
|
|
|
|
TEST_CASE("access_check", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
ebpf_security_generic_mapping_ptr sd_ptr;
|
|
ebpf_security_descriptor_t* sd = NULL;
|
|
DWORD sd_size = 0;
|
|
ebpf_security_generic_mapping_t generic_mapping{1, 1, 1};
|
|
auto allow_sddl = L"O:COG:BUD:(A;;FA;;;WD)";
|
|
auto deny_sddl = L"O:COG:BUD:(D;;FA;;;WD)";
|
|
REQUIRE(ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
allow_sddl, SDDL_REVISION_1, (PSECURITY_DESCRIPTOR*)&sd, &sd_size));
|
|
sd_ptr.reset(sd);
|
|
sd = nullptr;
|
|
|
|
REQUIRE(ebpf_validate_security_descriptor(sd_ptr.get(), sd_size) == EBPF_SUCCESS);
|
|
|
|
REQUIRE(ebpf_access_check(sd_ptr.get(), 1, &generic_mapping) == EBPF_SUCCESS);
|
|
|
|
REQUIRE(ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
deny_sddl, SDDL_REVISION_1, (PSECURITY_DESCRIPTOR*)&sd, &sd_size));
|
|
|
|
sd_ptr.reset(sd);
|
|
sd = nullptr;
|
|
|
|
REQUIRE(ebpf_validate_security_descriptor(sd_ptr.get(), sd_size) == EBPF_SUCCESS);
|
|
|
|
REQUIRE(ebpf_access_check(sd_ptr.get(), 1, &generic_mapping) == EBPF_ACCESS_DENIED);
|
|
}
|
|
|
|
struct ebpf_memory_descriptor_t_free
|
|
{
|
|
void
|
|
operator()(_Frees_ptr_opt_ ebpf_memory_descriptor_t* p)
|
|
{
|
|
ebpf_unmap_memory(p);
|
|
}
|
|
};
|
|
typedef std::unique_ptr<ebpf_memory_descriptor_t, ebpf_memory_descriptor_t_free> ebpf_memory_descriptor_ptr;
|
|
|
|
TEST_CASE("memory_map_test", "[platform]")
|
|
{
|
|
ebpf_memory_descriptor_ptr memory_descriptor;
|
|
memory_descriptor.reset(ebpf_map_memory(100));
|
|
REQUIRE(memory_descriptor);
|
|
REQUIRE(ebpf_protect_memory(memory_descriptor.get(), EBPF_PAGE_PROTECT_READ_WRITE) == EBPF_SUCCESS);
|
|
memset(ebpf_memory_descriptor_get_base_address(memory_descriptor.get()), 0xCC, 100);
|
|
REQUIRE(ebpf_protect_memory(memory_descriptor.get(), EBPF_PAGE_PROTECT_READ_ONLY) == EBPF_SUCCESS);
|
|
}
|
|
|
|
TEST_CASE("serialize_map_test", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
const int map_count = 10;
|
|
ebpf_map_info_internal_t internal_map_info_array[map_count] = {};
|
|
std::string pin_path_prefix = "\\ebpf\\map\\";
|
|
std::vector<std::string> pin_paths;
|
|
size_t buffer_length = 0;
|
|
uint8_t* buffer = nullptr;
|
|
size_t required_length;
|
|
size_t serialized_length;
|
|
ebpf_map_info_t* map_info_array;
|
|
|
|
// Construct the array of ebpf_map_info_internal_t to be serialized.
|
|
for (int i = 0; i < map_count; i++) {
|
|
pin_paths.push_back(pin_path_prefix + std::to_string(i));
|
|
}
|
|
|
|
for (int i = 0; i < map_count; i++) {
|
|
ebpf_map_info_internal_t* map_info = &internal_map_info_array[i];
|
|
map_info->definition.size = (i + 1) * 32;
|
|
map_info->definition.type = static_cast<ebpf_map_type_t>(i % (BPF_MAP_TYPE_ARRAY + 1));
|
|
map_info->definition.key_size = i + 1;
|
|
map_info->definition.value_size = (i + 1) * (i + 1);
|
|
map_info->definition.max_entries = (i + 1) * 128;
|
|
|
|
map_info->pin_path.length = pin_paths[i].size();
|
|
map_info->pin_path.value = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(pin_paths[i].c_str()));
|
|
}
|
|
|
|
// Serialize.
|
|
ebpf_result_t result = ebpf_serialize_internal_map_info_array(
|
|
map_count, internal_map_info_array, buffer, buffer_length, &serialized_length, &required_length);
|
|
REQUIRE(result == EBPF_INSUFFICIENT_BUFFER);
|
|
|
|
buffer = static_cast<uint8_t*>(calloc(required_length, 1));
|
|
REQUIRE(buffer != nullptr);
|
|
if (!buffer) {
|
|
return;
|
|
}
|
|
buffer_length = required_length;
|
|
|
|
result = ebpf_serialize_internal_map_info_array(
|
|
map_count, internal_map_info_array, buffer, buffer_length, &serialized_length, &required_length);
|
|
REQUIRE(result == EBPF_SUCCESS);
|
|
|
|
// Deserialize.
|
|
result = ebpf_deserialize_map_info_array(serialized_length, buffer, map_count, &map_info_array);
|
|
REQUIRE(result == EBPF_SUCCESS);
|
|
_Analysis_assume_(map_info_array != nullptr);
|
|
// Verify de-serialized map info array matches input.
|
|
for (int i = 0; i < map_count; i++) {
|
|
ebpf_map_info_internal_t* input_map_info = &internal_map_info_array[i];
|
|
ebpf_map_info_t* map_info = &map_info_array[i];
|
|
REQUIRE(
|
|
memcmp(&map_info->definition, &input_map_info->definition, sizeof(ebpf_map_definition_in_memory_t)) == 0);
|
|
REQUIRE(strnlen_s(map_info->pin_path, EBPF_MAX_PIN_PATH_LENGTH) == input_map_info->pin_path.length);
|
|
REQUIRE(memcmp(map_info->pin_path, input_map_info->pin_path.value, input_map_info->pin_path.length) == 0);
|
|
}
|
|
|
|
// Free de-serialized map info array.
|
|
ebpf_map_info_array_free(map_count, map_info_array);
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
TEST_CASE("serialize_program_info_test", "[platform]")
|
|
{
|
|
_test_helper test_helper;
|
|
|
|
ebpf_helper_function_prototype_t helper_prototype[] = {
|
|
{1000,
|
|
"helper_0",
|
|
EBPF_RETURN_TYPE_PTR_TO_MAP_VALUE_OR_NULL,
|
|
{EBPF_ARGUMENT_TYPE_PTR_TO_MAP, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_KEY}},
|
|
{1001,
|
|
"helper_1",
|
|
EBPF_RETURN_TYPE_INTEGER,
|
|
{EBPF_ARGUMENT_TYPE_PTR_TO_MAP, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_KEY, EBPF_ARGUMENT_TYPE_PTR_TO_MAP_VALUE}}};
|
|
// The values of the fields in the context_descriptor variable are completely arbitrary
|
|
// and have no effect on the test.
|
|
ebpf_context_descriptor_t context_descriptor = {32, 0, 8, -1};
|
|
GUID program_type_test = {0x7ebe418c, 0x76dd, 0x4c2c, {0x99, 0xbc, 0x5c, 0x48, 0xa2, 0x30, 0x4b, 0x90}};
|
|
ebpf_program_type_descriptor_t program_type = {"unit_test_program", &context_descriptor, program_type_test};
|
|
ebpf_program_info_t in_program_info = {program_type, EBPF_COUNT_OF(helper_prototype), helper_prototype};
|
|
|
|
size_t buffer_length = 0;
|
|
uint8_t* buffer = nullptr;
|
|
size_t required_length;
|
|
size_t serialized_length;
|
|
|
|
ebpf_program_info_t* out_program_info;
|
|
|
|
// Serialize.
|
|
ebpf_result_t result =
|
|
ebpf_serialize_program_info(&in_program_info, buffer, buffer_length, &serialized_length, &required_length);
|
|
REQUIRE(result == EBPF_INSUFFICIENT_BUFFER);
|
|
|
|
buffer = static_cast<uint8_t*>(calloc(required_length, 1));
|
|
_Analysis_assume_(buffer != nullptr);
|
|
buffer_length = required_length;
|
|
|
|
result = ebpf_serialize_program_info(&in_program_info, buffer, buffer_length, &serialized_length, &required_length);
|
|
REQUIRE(result == EBPF_SUCCESS);
|
|
|
|
// Deserialize.
|
|
result = ebpf_deserialize_program_info(serialized_length, buffer, &out_program_info);
|
|
REQUIRE(result == EBPF_SUCCESS);
|
|
|
|
// Verify de-serialized program info matches input.
|
|
REQUIRE(
|
|
in_program_info.program_type_descriptor.program_type == out_program_info->program_type_descriptor.program_type);
|
|
REQUIRE(
|
|
in_program_info.program_type_descriptor.is_privileged ==
|
|
out_program_info->program_type_descriptor.is_privileged);
|
|
REQUIRE(in_program_info.program_type_descriptor.context_descriptor != nullptr);
|
|
REQUIRE(
|
|
memcmp(
|
|
in_program_info.program_type_descriptor.context_descriptor,
|
|
out_program_info->program_type_descriptor.context_descriptor,
|
|
sizeof(ebpf_context_descriptor_t)) == 0);
|
|
REQUIRE(
|
|
strncmp(
|
|
in_program_info.program_type_descriptor.name,
|
|
out_program_info->program_type_descriptor.name,
|
|
EBPF_MAX_PROGRAM_DESCRIPTOR_NAME_LENGTH) == 0);
|
|
REQUIRE(in_program_info.count_of_helpers == out_program_info->count_of_helpers);
|
|
REQUIRE(out_program_info->helper_prototype != nullptr);
|
|
for (uint32_t i = 0; i < in_program_info.count_of_helpers; i++) {
|
|
ebpf_helper_function_prototype_t* in_prototype = &in_program_info.helper_prototype[i];
|
|
ebpf_helper_function_prototype_t* out_prototype = &out_program_info->helper_prototype[i];
|
|
REQUIRE(in_prototype->helper_id == out_prototype->helper_id);
|
|
REQUIRE(in_prototype->return_type == out_prototype->return_type);
|
|
for (int j = 0; j < _countof(in_prototype->arguments); j++)
|
|
REQUIRE(in_prototype->arguments[j] == out_prototype->arguments[j]);
|
|
REQUIRE(out_prototype->name != nullptr);
|
|
REQUIRE(strncmp(in_prototype->name, out_prototype->name, EBPF_MAX_HELPER_FUNCTION_NAME_LENGTH) == 0);
|
|
}
|
|
|
|
// Free de-serialized program info.
|
|
ebpf_program_info_free(out_program_info);
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
TEST_CASE("state_test", "[state]")
|
|
{
|
|
size_t allocated_index_1 = 0;
|
|
size_t allocated_index_2 = 0;
|
|
struct
|
|
{
|
|
uint32_t some_value;
|
|
} foo;
|
|
uintptr_t retreived_value = 0;
|
|
REQUIRE(ebpf_state_initiate() == EBPF_SUCCESS);
|
|
REQUIRE(ebpf_state_allocate_index(&allocated_index_1) == EBPF_SUCCESS);
|
|
REQUIRE(ebpf_state_allocate_index(&allocated_index_2) == EBPF_SUCCESS);
|
|
REQUIRE(allocated_index_2 != allocated_index_1);
|
|
REQUIRE(ebpf_state_store(allocated_index_1, reinterpret_cast<uintptr_t>(&foo)) == EBPF_SUCCESS);
|
|
REQUIRE(ebpf_state_load(allocated_index_1, &retreived_value) == EBPF_SUCCESS);
|
|
REQUIRE(retreived_value == reinterpret_cast<uintptr_t>(&foo));
|
|
ebpf_state_terminate();
|
|
} |