ebpf_program_t computes hash of program info on first attach (#1948)
* ebpf_program_t computes hash of program info on first attach Signed-off-by: Alan Jowett <alanjo@microsoft.com> * Provider reload test Signed-off-by: Alan Jowett <alanjo@microsoft.com> Signed-off-by: Alan Jowett <alanjo@microsoft.com> Co-authored-by: Anurag Saxena <43585259+saxena-anurag@users.noreply.github.com>
This commit is contained in:
Родитель
39c1490fb5
Коммит
44602dcb1d
|
@ -128,7 +128,7 @@ _ebpf_program_detach_links(_Inout_ ebpf_program_t* program)
|
|||
}
|
||||
|
||||
static ebpf_result_t
|
||||
_ebpf_program_verify_program_info_hash(_In_ const ebpf_program_t* program);
|
||||
_ebpf_program_initialize_or_verify_program_info_hash(_Inout_ ebpf_program_t* program);
|
||||
|
||||
static ebpf_result_t
|
||||
_ebpf_program_program_info_provider_changed(
|
||||
|
@ -207,17 +207,15 @@ _ebpf_program_program_info_provider_changed(
|
|||
goto Exit;
|
||||
}
|
||||
|
||||
if (program->parameters.program_info_hash != NULL) {
|
||||
return_value = _ebpf_program_verify_program_info_hash(program);
|
||||
if (return_value != EBPF_SUCCESS) {
|
||||
EBPF_LOG_MESSAGE_GUID(
|
||||
EBPF_TRACELOG_LEVEL_ERROR,
|
||||
EBPF_TRACELOG_KEYWORD_PROGRAM,
|
||||
"The program info used to verify the program doesn't match the program info provided by the "
|
||||
"extension",
|
||||
program->parameters.program_type);
|
||||
goto Exit;
|
||||
}
|
||||
return_value = _ebpf_program_initialize_or_verify_program_info_hash(program);
|
||||
if (return_value != EBPF_SUCCESS) {
|
||||
EBPF_LOG_MESSAGE_GUID(
|
||||
EBPF_TRACELOG_LEVEL_ERROR,
|
||||
EBPF_TRACELOG_KEYWORD_PROGRAM,
|
||||
"The program info used to verify the program doesn't match the program info provided by the "
|
||||
"extension",
|
||||
program->parameters.program_type);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
return_value = _ebpf_program_update_helpers(program);
|
||||
|
@ -491,6 +489,7 @@ ebpf_program_initialize(_Inout_ ebpf_program_t* program, _In_ const ebpf_program
|
|||
return_value = EBPF_INVALID_ARGUMENT;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if (program_parameters->program_name.length >= BPF_OBJ_NAME_LEN) {
|
||||
EBPF_LOG_MESSAGE_UINT64(
|
||||
EBPF_TRACELOG_LEVEL_ERROR,
|
||||
|
@ -1479,7 +1478,19 @@ _ebpf_helper_id_to_index_compare(const void* lhs, const void* rhs)
|
|||
|
||||
/**
|
||||
* @brief Compute the hash of the program info and compare it with the hash stored in the program. If the hash does not
|
||||
* match then the program was verified against the wrong program info.
|
||||
* match then the program was verified against the wrong program info. If the hash is not present then store the hash
|
||||
* in the program so it can be compared when the program information provider reattaches.
|
||||
*
|
||||
* Notes on why this works:
|
||||
* 1) The user application creates an ebpf_program_t object and sets the program type.
|
||||
* 2) During initialization, the program binds to the program information provider.
|
||||
* 3) During the attach callback, the program information is hashed and stored.
|
||||
* 4) The verifier then queries the program information from the ebpf_program_t object and uses it to verify the program
|
||||
* safety.
|
||||
* 5) If the program information provider is reattached, the program information is hashed and compared with the
|
||||
* hash stored in the program and the program is rejected if the hash does not match. This ensures that the program
|
||||
* information the verifier uses to verify the program safety is the same as the program information the program uses to
|
||||
* execute.
|
||||
*
|
||||
* @param[in] program Program to validate.
|
||||
* @param[in] program_info Program info to validate against.
|
||||
|
@ -1487,7 +1498,7 @@ _ebpf_helper_id_to_index_compare(const void* lhs, const void* rhs)
|
|||
* @return EBPF_INVALID_ARGUMENT the program info hash does not match.
|
||||
*/
|
||||
static ebpf_result_t
|
||||
_ebpf_program_verify_program_info_hash(_In_ const ebpf_program_t* program)
|
||||
_ebpf_program_initialize_or_verify_program_info_hash(_Inout_ ebpf_program_t* program)
|
||||
{
|
||||
EBPF_LOG_ENTRY();
|
||||
ebpf_result_t result;
|
||||
|
@ -1615,14 +1626,26 @@ _ebpf_program_verify_program_info_hash(_In_ const ebpf_program_t* program)
|
|||
goto Exit;
|
||||
}
|
||||
|
||||
if (output_hash_length != program->parameters.program_info_hash_length) {
|
||||
result = EBPF_INVALID_ARGUMENT;
|
||||
goto Exit;
|
||||
}
|
||||
if (program->parameters.program_info_hash_length == 0) {
|
||||
// This is the first time the program info hash is being computed.
|
||||
uint8_t* new_hash = ebpf_allocate(output_hash_length);
|
||||
if (new_hash == NULL) {
|
||||
result = EBPF_NO_MEMORY;
|
||||
goto Exit;
|
||||
}
|
||||
program->parameters.program_info_hash = new_hash;
|
||||
program->parameters.program_info_hash_length = output_hash_length;
|
||||
memcpy((uint8_t*)program->parameters.program_info_hash, hash, output_hash_length);
|
||||
} else {
|
||||
if (output_hash_length != program->parameters.program_info_hash_length) {
|
||||
result = EBPF_INVALID_ARGUMENT;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (memcmp(hash, program->parameters.program_info_hash, output_hash_length) != 0) {
|
||||
result = EBPF_INVALID_ARGUMENT;
|
||||
goto Exit;
|
||||
if (memcmp(hash, program->parameters.program_info_hash, output_hash_length) != 0) {
|
||||
result = EBPF_INVALID_ARGUMENT;
|
||||
goto Exit;
|
||||
}
|
||||
}
|
||||
|
||||
result = EBPF_SUCCESS;
|
||||
|
|
|
@ -78,8 +78,7 @@ _ebpf_extension_client_attach_provider(
|
|||
NTSTATUS status;
|
||||
ebpf_extension_client_t* local_client_context = (ebpf_extension_client_t*)client_context;
|
||||
ebpf_extension_client_binding_context_t* local_client_binding_context = NULL;
|
||||
|
||||
ExInitializeRundownProtection(&local_client_context->nmr_rundown_ref);
|
||||
bool rundown_ref_initialized = false;
|
||||
|
||||
// Check that the provider module Id matches the client's expected provider module Id.
|
||||
ebpf_assert(provider_registration_instance->ModuleId != NULL);
|
||||
|
@ -110,6 +109,10 @@ _ebpf_extension_client_attach_provider(
|
|||
goto Done;
|
||||
}
|
||||
|
||||
// Only initialize the rundown reference after we know we are going to try to attach.
|
||||
ExInitializeRundownProtection(&local_client_context->nmr_rundown_ref);
|
||||
rundown_ref_initialized = true;
|
||||
|
||||
local_client_binding_context =
|
||||
(ebpf_extension_client_binding_context_t*)ebpf_allocate(sizeof(ebpf_extension_client_binding_context_t));
|
||||
|
||||
|
@ -148,6 +151,10 @@ _ebpf_extension_client_attach_provider(
|
|||
}
|
||||
|
||||
Done:
|
||||
if (!NT_SUCCESS(status) && rundown_ref_initialized) {
|
||||
// Mark the nmr_rundown_ref as rundown if the attach fails.
|
||||
ExWaitForRundownProtectionRelease(&local_client_context->nmr_rundown_ref);
|
||||
}
|
||||
EBPF_RETURN_NTSTATUS(status);
|
||||
}
|
||||
|
||||
|
|
|
@ -2725,3 +2725,121 @@ TEST_CASE("test_ebpf_object_set_execution_type", "[end_to_end]")
|
|||
|
||||
bpf_object__close(jit_object);
|
||||
}
|
||||
|
||||
static void
|
||||
extension_reload_test(ebpf_execution_type_t execution_type)
|
||||
{
|
||||
_test_helper_end_to_end test_helper;
|
||||
// Create a 0-byte UDP packet.
|
||||
auto packet0 = prepare_udp_packet(0, ETHERNET_TYPE_IPV4);
|
||||
|
||||
// Test that we drop the packet and increment the map.
|
||||
xdp_md_t ctx0{packet0.data(), packet0.data() + packet0.size(), 0, TEST_IFINDEX};
|
||||
|
||||
// Try loading without the extension loaded.
|
||||
bpf_object* droppacket_object;
|
||||
int program_fd = -1;
|
||||
const char* error_message = nullptr;
|
||||
|
||||
// Should fail.
|
||||
REQUIRE(
|
||||
ebpf_program_load(
|
||||
execution_type == EBPF_EXECUTION_NATIVE ? "droppacket_um.dll" : "droppacket.o",
|
||||
BPF_PROG_TYPE_UNSPEC,
|
||||
execution_type,
|
||||
&droppacket_object,
|
||||
&program_fd,
|
||||
&error_message) != 0);
|
||||
|
||||
ebpf_free((void*)error_message);
|
||||
|
||||
// Load the program with the extension loaded.
|
||||
{
|
||||
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
|
||||
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP);
|
||||
|
||||
REQUIRE(
|
||||
ebpf_program_load(
|
||||
execution_type == EBPF_EXECUTION_NATIVE ? "droppacket_um.dll" : "droppacket.o",
|
||||
BPF_PROG_TYPE_UNSPEC,
|
||||
execution_type,
|
||||
&droppacket_object,
|
||||
&program_fd,
|
||||
&error_message) == 0);
|
||||
|
||||
uint32_t if_index = TEST_IFINDEX;
|
||||
bpf_link* link = nullptr;
|
||||
// Attach only to the single interface being tested.
|
||||
REQUIRE(hook.attach_link(program_fd, &if_index, sizeof(if_index), &link) == EBPF_SUCCESS);
|
||||
bpf_link__disconnect(link);
|
||||
bpf_link__destroy(link);
|
||||
|
||||
// Program should run.
|
||||
int hook_result;
|
||||
REQUIRE(hook.fire(&ctx0, &hook_result) == EBPF_SUCCESS);
|
||||
REQUIRE(hook_result == XDP_PASS);
|
||||
|
||||
// Unload the extension (xdp_program_info and hook will be destroyed).
|
||||
}
|
||||
|
||||
// Reload the extension provider with unchanged data.
|
||||
{
|
||||
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
|
||||
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP);
|
||||
|
||||
// Program should re-attach to the hook.
|
||||
|
||||
// Program should run.
|
||||
int hook_result;
|
||||
REQUIRE(hook.fire(&ctx0, &hook_result) == EBPF_SUCCESS);
|
||||
REQUIRE(hook_result == XDP_PASS);
|
||||
}
|
||||
|
||||
// Reload the extension provider with missing helper function.
|
||||
{
|
||||
ebpf_helper_function_addresses_t changed_helper_function_address_table =
|
||||
_test_ebpf_xdp_helper_function_address_table;
|
||||
ebpf_program_data_t changed_program_data = _ebpf_xdp_program_data;
|
||||
changed_program_data.program_type_specific_helper_function_addresses = &changed_helper_function_address_table;
|
||||
changed_helper_function_address_table.helper_function_count = 0;
|
||||
|
||||
ebpf_extension_data_t changed_provider_data = {
|
||||
TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(changed_program_data), &changed_program_data};
|
||||
|
||||
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
|
||||
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP, &changed_provider_data);
|
||||
|
||||
// Program should re-attach to the hook.
|
||||
|
||||
// Program should not run.
|
||||
int hook_result;
|
||||
REQUIRE(hook.fire(&ctx0, &hook_result) == EBPF_SUCCESS);
|
||||
REQUIRE(hook_result != XDP_PASS);
|
||||
}
|
||||
|
||||
// Reload the extension provider with changed helper function data.
|
||||
{
|
||||
ebpf_program_info_t changed_program_info = _ebpf_xdp_program_info;
|
||||
ebpf_helper_function_prototype_t helper_function_prototypes[] = {
|
||||
_xdp_ebpf_extension_helper_function_prototype[0]};
|
||||
helper_function_prototypes[0].return_type = EBPF_RETURN_TYPE_PTR_TO_MAP_VALUE_OR_NULL;
|
||||
changed_program_info.program_type_specific_helper_prototype = helper_function_prototypes;
|
||||
ebpf_program_data_t changed_program_data = _ebpf_xdp_program_data;
|
||||
changed_program_data.program_info = &changed_program_info;
|
||||
|
||||
ebpf_extension_data_t changed_provider_data = {
|
||||
TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(changed_program_data), &changed_program_data};
|
||||
|
||||
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
|
||||
program_info_provider_t xdp_program_info(EBPF_PROGRAM_TYPE_XDP, &changed_provider_data);
|
||||
|
||||
// Program should re-attach to the hook.
|
||||
|
||||
// Program should not run.
|
||||
int hook_result;
|
||||
REQUIRE(hook.fire(&ctx0, &hook_result) == EBPF_SUCCESS);
|
||||
REQUIRE(hook_result != XDP_PASS);
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_ALL_TEST_CASES("extension_reload_test", "[end_to_end]", extension_reload_test);
|
||||
|
|
|
@ -434,10 +434,12 @@ static ebpf_extension_data_t _test_ebpf_sample_extension_program_info_provider_d
|
|||
typedef class _program_info_provider
|
||||
{
|
||||
public:
|
||||
_program_info_provider(ebpf_program_type_t program_type)
|
||||
_program_info_provider(ebpf_program_type_t program_type, ebpf_extension_data_t* custom_provider_data = nullptr)
|
||||
: program_type(program_type), provider(nullptr), provider_data(nullptr)
|
||||
{
|
||||
if (program_type == EBPF_PROGRAM_TYPE_XDP) {
|
||||
if (custom_provider_data != nullptr) {
|
||||
provider_data = custom_provider_data;
|
||||
} else if (program_type == EBPF_PROGRAM_TYPE_XDP) {
|
||||
provider_data = &_ebpf_xdp_program_info_provider_data;
|
||||
} else if (program_type == EBPF_PROGRAM_TYPE_BIND) {
|
||||
provider_data = &_ebpf_bind_program_info_provider_data;
|
||||
|
|
Загрузка…
Ссылка в новой задаче