Skip to content

Commit

Permalink
Permit upper and lower IRQL bounds
Browse files Browse the repository at this point in the history
Signed-off-by: Alan Jowett <[email protected]>
  • Loading branch information
Alan Jowett committed Jan 5, 2024
1 parent cb7e0ea commit 16066de
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 30 deletions.
1 change: 1 addition & 0 deletions docs/eBpfExtensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ functions that override the global helper functions provided by the eBPF runtime
structure from provided data and context buffers.
* `context_destroy`: Pointer to `ebpf_program_context_destroy_t` function that destroys a program type specific
context structure and populates the returned data and context buffers.
* `minimum_irql`: The lowest IRQL at which the eBPF program can be invoked.
* `maximum_irql`: The highest IRQL at which the eBPF program can be invoked.

#### `ebpf_program_info_t` Struct
Expand Down
1 change: 1 addition & 0 deletions include/ebpf_program_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ typedef struct _ebpf_program_data
global_helper_function_addresses; ///< Pointer to global helper function addresses being overriden.
ebpf_program_context_create_t context_create; ///< Pointer to context create function.
ebpf_program_context_destroy_t context_destroy; ///< Pointer to context destroy function.
uint8_t minimum_irql; ///< Minimum IRQL at which the program can be invoked.
uint8_t maximum_irql; ///< Maximum IRQL at which the program can be invoked.
} ebpf_program_data_t;

Expand Down
9 changes: 5 additions & 4 deletions libs/execution_context/ebpf_link.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,9 @@ _ebpf_link_instance_invoke_batch_begin(
provider_reference_held = true;

uint8_t maximum_irql;
uint8_t minimum_irql;

return_value = ebpf_program_get_maximum_irql(link->program, &maximum_irql);
return_value = ebpf_program_get_irql_range(link->program, &minimum_irql, &maximum_irql);
if (return_value != EBPF_SUCCESS) {
EBPF_LOG_MESSAGE_ERROR(
EBPF_TRACELOG_LEVEL_ERROR,
Expand All @@ -519,12 +520,12 @@ _ebpf_link_instance_invoke_batch_begin(
goto Done;
}

if (execution_context_state->current_irql > maximum_irql) {
EBPF_LOG_MESSAGE_UINT64_UINT64(
if ((execution_context_state->current_irql > maximum_irql) ||
(execution_context_state->current_irql < minimum_irql)) {
EBPF_LOG_MESSAGE_UINT64(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_LINK,
"Program cannot be invoked at current IRQL.",
maximum_irql,
execution_context_state->current_irql);
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
Expand Down
12 changes: 7 additions & 5 deletions libs/execution_context/ebpf_program.c
Original file line number Diff line number Diff line change
Expand Up @@ -2169,7 +2169,7 @@ typedef struct _ebpf_program_test_run_context
ebpf_program_data_t* program_data;
void* context;
ebpf_program_test_run_options_t* options;
uint8_t maximum_irql;
uint8_t required_irql;
bool canceled;
void* async_context;
void* completion_context;
Expand Down Expand Up @@ -2206,7 +2206,7 @@ _ebpf_program_test_run_work_item(_In_ cxplat_preemptible_work_item_t* work_item,

ebpf_epoch_synchronize();

old_irql = ebpf_raise_irql(context->maximum_irql);
old_irql = ebpf_raise_irql(context->required_irql);
irql_raised = true;

ebpf_epoch_enter(&epoch_state);
Expand Down Expand Up @@ -2242,7 +2242,7 @@ _ebpf_program_test_run_work_item(_In_ cxplat_preemptible_work_item_t* work_item,
ebpf_lower_irql(old_irql);

// Reacquire the CPU.
old_irql = ebpf_raise_irql(context->maximum_irql);
old_irql = ebpf_raise_irql(context->required_irql);

// Reset the start time.
start_time = ebpf_query_time_since_boot(false);
Expand Down Expand Up @@ -2352,7 +2352,7 @@ ebpf_program_execute_test_run(

test_run_context->program = program;
test_run_context->program_data = program_data;
test_run_context->maximum_irql = program_data->maximum_irql;
test_run_context->required_irql = program_data->maximum_irql;
test_run_context->context = context;
test_run_context->options = options;
test_run_context->async_context = async_context;
Expand Down Expand Up @@ -2441,13 +2441,15 @@ ebpf_program_get_state_index()
}

_Must_inspect_result_ ebpf_result_t
ebpf_program_get_maximum_irql(_In_ const ebpf_program_t* program, _Out_ uint8_t* maximum_irql)
ebpf_program_get_irql_range(
_In_ const ebpf_program_t* program, _Out_ uint8_t* minimum_irql, _Out_ uint8_t* maximum_irql)
{
if (program->info_extension_provider_data == NULL) {
return EBPF_INVALID_ARGUMENT;
}

ebpf_program_data_t* program_data = (ebpf_program_data_t*)program->info_extension_provider_data->data;
*minimum_irql = program_data->minimum_irql;
*maximum_irql = program_data->maximum_irql;
return EBPF_SUCCESS;
}
6 changes: 4 additions & 2 deletions libs/execution_context/ebpf_program.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,15 +411,17 @@ extern "C"
ebpf_program_get_state_index();

/**
* @brief Get the maximum IRQL at which the program can be invoked.
* @brief Get the minimum and maximum IRQL at which the program can be invoked.
*
* @param[in] program The program to query.
* @param[out] minimum_irql The minimum IRQL at which the program can be invoked.
* @param[out] maximum_irql The maximum IRQL at which the program can be invoked.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_INVALID_ARGUMENT The program is not in a valid state.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_program_get_maximum_irql(_In_ const ebpf_program_t* program, _Out_ uint8_t* maximum_irql);
ebpf_program_get_irql_range(
_In_ const ebpf_program_t* program, _Out_ uint8_t* minimum_irql, _Out_ uint8_t* maximum_irql);

#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions netebpfext/net_ebpf_ext_bind.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ static ebpf_program_data_t _ebpf_bind_program_data = {
.program_info = &_ebpf_bind_program_info,
.context_create = _ebpf_bind_context_create,
.context_destroy = _ebpf_bind_context_destroy,
.minimum_irql = PASSIVE_LEVEL,
.maximum_irql = PASSIVE_LEVEL,
};

Expand Down
1 change: 1 addition & 0 deletions netebpfext/net_ebpf_ext_sock_addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ static ebpf_program_data_t _ebpf_sock_addr_program_data = {
.global_helper_function_addresses = &_ebpf_sock_addr_global_helper_function_address_table,
.context_create = &_ebpf_sock_addr_context_create,
.context_destroy = &_ebpf_sock_addr_context_destroy,
.minimum_irql = PASSIVE_LEVEL,
.maximum_irql = DISPATCH_LEVEL,
};

Expand Down
1 change: 1 addition & 0 deletions netebpfext/net_ebpf_ext_sock_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ static ebpf_program_data_t _ebpf_sock_ops_program_data = {
.program_info = &_ebpf_sock_ops_program_info,
.context_create = &_ebpf_sock_ops_context_create,
.context_destroy = &_ebpf_sock_ops_context_destroy,
.minimum_irql = PASSIVE_LEVEL,
.maximum_irql = DISPATCH_LEVEL,
};

Expand Down
1 change: 1 addition & 0 deletions netebpfext/net_ebpf_ext_xdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ static ebpf_program_data_t _ebpf_xdp_test_program_data = {
.program_type_specific_helper_function_addresses = &_ebpf_xdp_test_helper_function_address_table,
.context_create = _ebpf_xdp_context_create,
.context_destroy = _ebpf_xdp_context_delete,
.minimum_irql = DISPATCH_LEVEL,
.maximum_irql = DISPATCH_LEVEL,
};

Expand Down
112 changes: 112 additions & 0 deletions tests/end_to_end/end_to_end.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ droppacket_test(ebpf_execution_type_t execution_type)
bpf_object_ptr unique_object;
fd_t program_fd;
bpf_link_ptr link;
emulate_dpc_t dpc(0);

single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
REQUIRE(hook.initialize() == EBPF_SUCCESS);
Expand Down Expand Up @@ -506,6 +507,111 @@ droppacket_test(ebpf_execution_type_t execution_type)
bpf_object__close(unique_object.release());
}

void
irql_low_test(ebpf_execution_type_t execution_type)
{
_test_helper_end_to_end test_helper;
test_helper.initialize();

int result;
const char* error_message = nullptr;
bpf_object_ptr unique_object;
fd_t program_fd;
bpf_link_ptr link;

single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP);
REQUIRE(hook.initialize() == EBPF_SUCCESS);
program_info_provider_t xdp_program_info;
REQUIRE(xdp_program_info.initialize(EBPF_PROGRAM_TYPE_XDP) == EBPF_SUCCESS);

const char* file_name = (execution_type == EBPF_EXECUTION_NATIVE ? "droppacket_um.dll" : "droppacket.o");
result =
ebpf_program_load(file_name, BPF_PROG_TYPE_UNSPEC, execution_type, &unique_object, &program_fd, &error_message);

if (error_message) {
printf("ebpf_program_load failed with %s\n", error_message);
ebpf_free((void*)error_message);
}
REQUIRE(result == 0);
fd_t dropped_packet_map_fd = bpf_object__find_map_fd_by_name(unique_object.get(), "dropped_packet_map");

// Tell the program which interface to filter on.
fd_t interface_index_map_fd = bpf_object__find_map_fd_by_name(unique_object.get(), "interface_index_map");
uint32_t key = 0;
uint32_t if_index = TEST_IFINDEX;
REQUIRE(bpf_map_update_elem(interface_index_map_fd, &key, &if_index, EBPF_ANY) == EBPF_SUCCESS);

// Attach only to the single interface being tested.
REQUIRE(hook.attach_link(program_fd, &if_index, sizeof(if_index), &link) == EBPF_SUCCESS);

// Create a 0-byte UDP packet.
auto packet0 = prepare_udp_packet(0, ETHERNET_TYPE_IPV4);

uint64_t value = 1000;
REQUIRE(bpf_map_update_elem(dropped_packet_map_fd, &key, &value, EBPF_ANY) == EBPF_SUCCESS);

// Test that we drop the packet and increment the map
xdp_md_t ctx0{packet0.data(), packet0.data() + packet0.size(), 0, TEST_IFINDEX};

uint32_t hook_result;
REQUIRE(hook.fire(&ctx0, &hook_result) == EBPF_INVALID_ARGUMENT);

// Should not have run due IRQL too low.
REQUIRE(bpf_map_lookup_elem(dropped_packet_map_fd, &key, &value) == EBPF_SUCCESS);
REQUIRE(value == 1000);

hook.detach_and_close_link(&link);

bpf_object__close(unique_object.release());
}

void
irql_high_test(ebpf_execution_type_t execution_type)
{
_test_helper_end_to_end test_helper;
test_helper.initialize();

const char* error_message = nullptr;
int result;
bpf_object_ptr unique_object;
bpf_link_ptr link;
fd_t program_fd;

program_info_provider_t bind_program_info;
REQUIRE(bind_program_info.initialize(EBPF_PROGRAM_TYPE_BIND) == EBPF_SUCCESS);

// Note: We are deliberately using "bindmonitor_um.dll" here as we want the programs to be loaded from
// the individual dll, instead of the combined DLL. This helps in testing the DLL stub which is generated
// bpf2c.exe tool.
const char* file_name = (execution_type == EBPF_EXECUTION_NATIVE ? "bindmonitor_um.dll" : "bindmonitor.o");

result =
ebpf_program_load(file_name, BPF_PROG_TYPE_UNSPEC, execution_type, &unique_object, &program_fd, &error_message);

if (error_message) {
printf("ebpf_program_load failed with %s\n", error_message);
ebpf_free((void*)error_message);
}
REQUIRE(result == 0);

single_instance_hook_t hook(EBPF_PROGRAM_TYPE_BIND, EBPF_ATTACH_TYPE_BIND);
REQUIRE(hook.initialize() == EBPF_SUCCESS);

uint32_t ifindex = 0;
REQUIRE(hook.attach_link(program_fd, &ifindex, sizeof(ifindex), &link) == EBPF_SUCCESS);

bind_md_t ctx = {};

emulate_dpc_t emulate_dpc(0);

uint32_t result_value;
REQUIRE(hook.fire(&ctx, &result_value) == EBPF_INVALID_ARGUMENT);

hook.detach_and_close_link(&link);

bpf_object__close(unique_object.release());
}

// See also divide_by_zero_test_km in api_test.cpp for the kernel-mode equivalent.
void
divide_by_zero_test_um(ebpf_execution_type_t execution_type)
Expand Down Expand Up @@ -1013,6 +1119,8 @@ DECLARE_ALL_TEST_CASES("bindmonitor-ringbuf", "[end_to_end]", bindmonitor_ring_b
DECLARE_ALL_TEST_CASES("utility-helpers", "[end_to_end]", _utility_helper_functions_test);
DECLARE_ALL_TEST_CASES("map", "[end_to_end]", map_test);
DECLARE_ALL_TEST_CASES("bad_map_name", "[end_to_end]", bad_map_name_um);
DECLARE_ALL_TEST_CASES("irql_low_test", "[end_to_end]", irql_low_test);
DECLARE_ALL_TEST_CASES("irql_high_test", "[end_to_end]", irql_high_test);

TEST_CASE("enum section", "[end_to_end]")
{
Expand Down Expand Up @@ -1691,6 +1799,8 @@ _xdp_reflect_packet_test(ebpf_execution_type_t execution_type, ADDRESS_FAMILY ad
{
_test_helper_end_to_end test_helper;
test_helper.initialize();
emulate_dpc_t dpc(0);

single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP_TEST, EBPF_ATTACH_TYPE_XDP_TEST);
REQUIRE(hook.initialize() == EBPF_SUCCESS);
program_info_provider_t xdp_program_info;
Expand Down Expand Up @@ -1736,6 +1846,7 @@ _xdp_encap_reflect_packet_test(ebpf_execution_type_t execution_type, ADDRESS_FAM
{
_test_helper_end_to_end test_helper;
test_helper.initialize();
emulate_dpc_t dpc(0);
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP_TEST, EBPF_ATTACH_TYPE_XDP_TEST);
REQUIRE(hook.initialize() == EBPF_SUCCESS);
program_info_provider_t xdp_program_info;
Expand Down Expand Up @@ -1865,6 +1976,7 @@ _xdp_decapsulate_permit_packet_test(ebpf_execution_type_t execution_type, ADDRES
{
_test_helper_end_to_end test_helper;
test_helper.initialize();
emulate_dpc_t dpc(0);
single_instance_hook_t hook(EBPF_PROGRAM_TYPE_XDP_TEST, EBPF_ATTACH_TYPE_XDP_TEST);
REQUIRE(hook.initialize() == EBPF_SUCCESS);
program_info_provider_t xdp_program_info;
Expand Down
54 changes: 36 additions & 18 deletions tests/end_to_end/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -629,40 +629,56 @@ static const ebpf_program_info_t _mock_xdp_program_info = {
_xdp_test_ebpf_extension_helper_function_prototype};

static ebpf_program_data_t _mock_xdp_program_data = {
&_mock_xdp_program_info,
&_mock_xdp_helper_function_address_table,
nullptr,
_xdp_context_create,
_xdp_context_destroy};
.program_info = &_mock_xdp_program_info,
.program_type_specific_helper_function_addresses = &_mock_xdp_helper_function_address_table,
.context_create = _xdp_context_create,
.context_destroy = _xdp_context_destroy,
.minimum_irql = DISPATCH_LEVEL,
.maximum_irql = DISPATCH_LEVEL,
};

static ebpf_extension_data_t _mock_xdp_program_info_provider_data = {
TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(_mock_xdp_program_data), &_mock_xdp_program_data};

// XDP_TEST.
static ebpf_program_data_t _ebpf_xdp_test_program_data = {
&_ebpf_xdp_test_program_info,
&_mock_xdp_helper_function_address_table,
nullptr,
_xdp_context_create,
_xdp_context_destroy};
.program_info = &_ebpf_xdp_test_program_info,
.program_type_specific_helper_function_addresses = &_mock_xdp_helper_function_address_table,
.context_create = _xdp_context_create,
.context_destroy = _xdp_context_destroy,
.minimum_irql = DISPATCH_LEVEL,
.maximum_irql = DISPATCH_LEVEL,
};

static ebpf_extension_data_t _ebpf_xdp_test_program_info_provider_data = {
TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(_ebpf_xdp_test_program_data), &_ebpf_xdp_test_program_data};

// Bind.
static ebpf_program_data_t _ebpf_bind_program_data = {&_ebpf_bind_program_info, NULL};
static ebpf_program_data_t _ebpf_bind_program_data = {
.program_info = &_ebpf_bind_program_info,
.minimum_irql = PASSIVE_LEVEL,
.maximum_irql = PASSIVE_LEVEL,
};

static ebpf_extension_data_t _ebpf_bind_program_info_provider_data = {
TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(_ebpf_bind_program_data), &_ebpf_bind_program_data};

// CGROUP_SOCK_ADDR.
static ebpf_program_data_t _ebpf_sock_addr_program_data = {&_ebpf_sock_addr_program_info, NULL};
static ebpf_program_data_t _ebpf_sock_addr_program_data = {
.program_info = &_ebpf_sock_addr_program_info,
.minimum_irql = PASSIVE_LEVEL,
.maximum_irql = DISPATCH_LEVEL,
};

static ebpf_extension_data_t _ebpf_sock_addr_program_info_provider_data = {
TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(_ebpf_sock_addr_program_data), &_ebpf_sock_addr_program_data};

// SOCK_OPS.
static ebpf_program_data_t _ebpf_sock_ops_program_data = {&_ebpf_sock_ops_program_info, NULL};
static ebpf_program_data_t _ebpf_sock_ops_program_data = {
.program_info = &_ebpf_sock_ops_program_info,
.minimum_irql = PASSIVE_LEVEL,
.maximum_irql = DISPATCH_LEVEL,
};

static ebpf_extension_data_t _ebpf_sock_ops_program_info_provider_data = {
TEST_NET_EBPF_EXTENSION_NPI_PROVIDER_VERSION, sizeof(_ebpf_sock_ops_program_data), &_ebpf_sock_ops_program_data};
Expand All @@ -682,11 +698,13 @@ static ebpf_helper_function_addresses_t _test_global_helper_function_address_tab
EBPF_COUNT_OF(_test_global_helper_functions), (uint64_t*)_test_global_helper_functions};

static ebpf_program_data_t _test_ebpf_sample_extension_program_data = {
&_sample_ebpf_extension_program_info,
&_sample_ebpf_ext_helper_function_address_table,
&_test_global_helper_function_address_table,
_sample_test_context_create,
_sample_test_context_destroy};
.program_info = &_sample_ebpf_extension_program_info,
.program_type_specific_helper_function_addresses = &_sample_ebpf_ext_helper_function_address_table,
.global_helper_function_addresses = &_test_global_helper_function_address_table,
.context_create = _sample_test_context_create,
.context_destroy = _sample_test_context_destroy,
.minimum_irql = PASSIVE_LEVEL,
.maximum_irql = DISPATCH_LEVEL};

#define TEST_EBPF_SAMPLE_EXTENSION_NPI_PROVIDER_VERSION 0

Expand Down
Loading

0 comments on commit 16066de

Please sign in to comment.