Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement implicit_context for helper functions. #3656

Merged
merged 46 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
5b21e46
backup
saxena-anurag Apr 23, 2024
f2812bc
backup
saxena-anurag May 6, 2024
40346b3
fix build issues
saxena-anurag Jun 18, 2024
a14a07e
fix build
saxena-anurag Jun 19, 2024
66e812c
fix analysis failures, fix tests
saxena-anurag Jun 20, 2024
6a14c3f
seperate out sample for implicit context
saxena-anurag Jun 20, 2024
c1aab90
backup
saxena-anurag Jun 24, 2024
d2c2b0e
add ctx as last argument
saxena-anurag Jun 24, 2024
f072525
fix hash
saxena-anurag Jun 25, 2024
3a728ef
block implicit context for jit and interpret
saxena-anurag Jun 26, 2024
2780c14
stabilize tests
saxena-anurag Jun 26, 2024
cd690e6
Merge branch 'main' into user/anusa/implicit_ctx
saxena-anurag Jun 26, 2024
53648a6
tail call fast path
saxena-anurag Jun 27, 2024
aa29fd9
Merge branch 'main' into user/anusa/implicit_ctx
saxena-anurag Jun 27, 2024
6d1fddb
add check to reject change in context_header support
saxena-anurag Jun 28, 2024
87547a8
enable implicit context for jit and interpret
saxena-anurag Jun 28, 2024
d92316e
fix
saxena-anurag Jul 1, 2024
26bfaf0
fix build
saxena-anurag Jul 1, 2024
6e251f7
fix analysis build
saxena-anurag Jul 1, 2024
aeaa633
update version, update expected files
saxena-anurag Jul 1, 2024
0fdce04
fix test failures
saxena-anurag Jul 1, 2024
33cc42f
Merge branch 'main' into user/anusa/implicit_ctx
saxena-anurag Jul 1, 2024
7411c6b
fix test failures
saxena-anurag Jul 1, 2024
1ea7026
fix tests
saxena-anurag Jul 2, 2024
dc71fc2
cleanup
saxena-anurag Jul 2, 2024
1bdb594
cleanup
saxena-anurag Jul 2, 2024
ce6a01b
Merge branch 'main' into user/anusa/implicit_ctx
saxena-anurag Jul 2, 2024
a5a2e33
more cleanup
saxena-anurag Jul 3, 2024
84e97ab
enable performance tests
saxena-anurag Jul 4, 2024
46e4e7b
enable performance tests
saxena-anurag Jul 4, 2024
f96510d
remove tail_call changes
saxena-anurag Jul 13, 2024
7e99371
remove tail_call changes
saxena-anurag Jul 13, 2024
2f0dbf3
remove tail_call changes
saxena-anurag Jul 13, 2024
c940bbb
remove tail_call changes
saxena-anurag Jul 13, 2024
289e5d8
fix analysis error
saxena-anurag Jul 14, 2024
202f72e
Merge branch 'main' into user/anusa/implicit_ctx
saxena-anurag Jul 14, 2024
0272efa
update documentation
saxena-anurag Jul 15, 2024
88784c8
code cleanup
saxena-anurag Jul 15, 2024
0b30763
Merge branch 'main' into user/anusa/implicit_ctx
saxena-anurag Jul 15, 2024
695d62f
fix bad merge
saxena-anurag Jul 16, 2024
86b15c1
fix test case
saxena-anurag Jul 16, 2024
dc60a09
cr comments
saxena-anurag Jul 16, 2024
6a4e3c4
update expected files
saxena-anurag Jul 16, 2024
0fb7b8d
Merge branch 'main' into user/anusa/implicit_ctx
saxena-anurag Jul 16, 2024
9b65b9d
Merge branch 'main' into user/anusa/implicit_ctx
saxena-anurag Jul 19, 2024
f119d0b
cr comments
saxena-anurag Jul 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion docs/eBpfExtensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,29 @@ helper function.
* `arguments`: Array of (at most) five helper function arguments of type `ebpf_argument_type_t`.
* `flags`: Bit field of flags.
* `reallocate_packet`: Flag indicating if this helper function performs packet reallocation.
* `implicit_context`: Flag indicating the extension requires implicit context for this helper function.

**Note about `implicit_context`**:
With the `implicit_context` feature, an extension can choose to get the program context as the 6th argument to the
helper function. In case the helper function does not require all the original 5 arguments (program context being
the 6th argument), the helper function should declare dummy arguments as placeholders for the unused arguments.
Note that this new change does not require any change in the helper function prototype that is needed for the
program verification.

**Example**
Below is an example of a helper function only takes 1 argument `arg` as input, but to get also program context
as input, also declares the remaining 4 dummy arguments (`dummy_param1` to `dummy_param4`).

```c
static int64_t
sample_ebpf_extension_helper_implicit_2(
uint32_t arg,
uint64_t dummy_param1,
uint64_t dummy_param2,
uint64_t dummy_param3,
uint64_t dummy_param4,
_In_ const sample_program_context_t* context)
```

#### `ebpf_argument_type_t` Enum
This enum describes the various argument types that can be passed to an eBPF helper function. This is defined in the
Expand Down Expand Up @@ -285,7 +307,19 @@ If the change in data structure is such that it is no longer backward compatible
then the version number will be updated. In this case, the product version of eBPF for Windows must be updated to indicate a breaking change
as well. Existing eBPF extensions would need to be re-compiled to work with the latest version of eBPF.

#### 2.2.1 Hashing of data structures to validate verification of native images
#### 2.2.1 Backward / Forward compatibility
For the cases when a new feature is exposed from eBPF to the extensions that is backward compatible, and requires a bit field / flag to be
added to one of the extension data structures, there are 2 possible options:
1. Add a new variable in the structure corresponding to the new feature. This will result in increasing the size of the struct.
2. Utilize a bit field in an (if available) existing `flags` field. This option does not result in increasing the size of the struct.

Which of the above 2 options to choose will depend if the feature is forward compatible. If the new feature is forward compatible, (i.e.
an extension compiled with new eBPF headers can work fine with an older eBPF runtime), then any of the above 2 options can be chosen to
add the flag. However, if the feature is not forward compatible, and will cause functional issues or crashes (when a new extension is
deployed with older eBPF runtime), then option 1 **MUST** be chosen. Choosing option 1 results in and increase of the size of the struct,
allowing older eBPF runtime to detect an incompatible extension, and reject binding to such an extension.

#### 2.2.2 Hashing of data structures to validate verification of native images
When native images are generated, bpf2c uses the verifier to ensure that the program is safe to execute and then
computes a hash over the invariants used to validate the program. These invariants include the properties of the
program information provider and the signature of any helper functions used. The following fields are included in the hash:
Expand Down
4 changes: 3 additions & 1 deletion include/bpf2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ extern "C"
#define htole64(X) (X)
#endif

typedef uint64_t (*helper_function_t)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, void*);

/**
* @brief Helper function entry.
* This structure defines a helper function entry in the metadata table. The address of the helper function is
Expand All @@ -53,7 +55,7 @@ extern "C"
*/
typedef struct _helper_function_entry
{
uint64_t (*address)(uint64_t r1, uint64_t r2, uint64_t r3, uint64_t r4, uint64_t r5);
helper_function_t address;
uint32_t helper_id;
const char* name;
bool tail_call;
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 @@ -36,6 +36,7 @@ typedef struct _ebpf_helper_function_prototype
ebpf_return_type_t return_type;
ebpf_argument_type_t arguments[5];
ebpf_helper_function_prototype_flags_t flags;
bool implicit_context;
Copy link
Contributor Author

@saxena-anurag saxena-anurag Jul 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note:
implicit_context is added as a separate bool instead of adding a bit field in existing ebpf_helper_function_prototype_flags_t flags to handle forward compatibility issues.

Details:
The implicit_context change in this PR is optional and backward compatible but is not forward compatible. This means that if an extension is compiled with an older (<= 0.17.x) version of eBPF headers and is deployed on a machine running a newer (>= 0.18.x) version of eBPF runtime, it can safely run with no issues.

On the other hand, if the extension is compiled with a newer version of eBPF and supports implicit context for any of its helper functions, and gets deployed with older eBPF runtime, it can cause a crash unless eBPF runtime rejects binding with such an extension.

Adding a new implicit_context field in ebpf_helper_function_prototype_t changes the size of the struct allowing eBPF runtime o detect and reject incompatible version of extension.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with blocking this, but I am not sure overloading the verification check as being the correct approach.

Copy link
Collaborator

@matthewige matthewige Jul 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-opening as I have a question about this - In the future, when would it be appropriate to add to the flags field vs requiring a new bool?

Do we maybe want to add documentation to help add clarity for future developers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think if the new feature is both backward and forward compatible, it can be added to the existing flags. Agree that we can probably document this somewhere, but not sure which doc (ebpfExtension.md is ideally for extension developers, but this will be for eBPF developers). I can maybe add a comment in this file itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated doc to mention this point.

} ebpf_helper_function_prototype_t;

// This is the type definition for the eBPF program information
Expand Down
2 changes: 1 addition & 1 deletion include/ebpf_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ typedef enum _ebpf_helper_function

#define EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION 1
#define EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION_SIZE \
EBPF_SIZE_INCLUDING_FIELD(ebpf_helper_function_prototype_t, flags)
EBPF_SIZE_INCLUDING_FIELD(ebpf_helper_function_prototype_t, implicit_context)
#define EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION_TOTAL_SIZE sizeof(ebpf_helper_function_prototype_t)
#define EBPF_HELPER_FUNCTION_PROTOTYPE_HEADER \
{ \
Expand Down
2 changes: 1 addition & 1 deletion libs/execution_context/ebpf_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ ebpf_core_resolve_helper(
ebpf_handle_t program_handle,
const size_t count_of_helpers,
_In_reads_(count_of_helpers) const uint32_t* helper_function_ids,
_Out_writes_(count_of_helpers) uint64_t* helper_function_addresses)
_Out_writes_(count_of_helpers) helper_function_address_t* helper_function_addresses)
{
EBPF_LOG_ENTRY();
ebpf_program_t* program = NULL;
Expand Down
2 changes: 1 addition & 1 deletion libs/execution_context/ebpf_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ extern "C"
ebpf_handle_t program_handle,
const size_t count_of_helpers,
_In_reads_(count_of_helpers) const uint32_t* helper_function_ids,
_Out_writes_(count_of_helpers) uint64_t* helper_function_addresses);
_Out_writes_(count_of_helpers) helper_function_address_t* helper_function_addresses);

/**
* @brief Close the FsContext2 from a file object.
Expand Down
62 changes: 48 additions & 14 deletions libs/execution_context/ebpf_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ static const uint32_t _ebpf_native_marker = 'entv';
// Set this value if there is a need to block older version of the native driver.
static bpf2c_version_t _ebpf_minimum_version = {0, 0, 0};

// Minimum bpf2c version that supports implicit context.
static const bpf2c_version_t _ebpf_version_implicit_context = {0, 18, 0};

#ifndef __CGUID_H__
static const GUID GUID_NULL = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
#endif

typedef uint64_t (*helper_function_address)(uint64_t r1, uint64_t r2, uint64_t r3, uint64_t r4, uint64_t r5);
typedef uint64_t (*helper_function_address)(
uint64_t r1, uint64_t r2, uint64_t r3, uint64_t r4, uint64_t r5, void* context);

typedef struct _ebpf_native_map
{
Expand Down Expand Up @@ -74,6 +78,7 @@ typedef struct _ebpf_native_module
HANDLE nmr_binding_handle;
ebpf_list_entry_t list_entry;
cxplat_preemptible_work_item_t* cleanup_work_item;
bpf2c_version_t version;
} ebpf_native_module_t;

static const GUID _ebpf_native_npi_id = {/* c847aac8-a6f2-4b53-aea3-f4a94b9a80cb */
Expand Down Expand Up @@ -125,7 +130,7 @@ void
ebpf_native_unload_driver(_In_z_ const wchar_t* service_name);

static int
_ebpf_compare_versions(bpf2c_version_t* lhs, bpf2c_version_t* rhs)
_ebpf_compare_versions(_In_ const bpf2c_version_t* lhs, _In_ const bpf2c_version_t* rhs)
{
if (lhs->major < rhs->major) {
return -1;
Expand Down Expand Up @@ -158,7 +163,7 @@ typedef struct _ebpf_native_helper_address_changed_context

static ebpf_result_t
_ebpf_native_helper_address_changed(
size_t address_count, _In_reads_opt_(address_count) uintptr_t* addresses, _In_opt_ void* context);
size_t address_count, _In_reads_opt_(address_count) helper_function_address_t* addresses, _In_opt_ void* context);

static void
_ebpf_native_unload_work_item(_In_ cxplat_preemptible_work_item_t* work_item, _In_opt_ const void* service)
Expand Down Expand Up @@ -479,9 +484,8 @@ _ebpf_native_provider_attach_client_callback(
goto Done;
}

bpf2c_version_t client_version = {0, 0, 0};
table->version(&client_version);
if (_ebpf_compare_versions(&client_version, &_ebpf_minimum_version) < 0) {
table->version(&client_context->version);
if (_ebpf_compare_versions(&client_context->version, &_ebpf_minimum_version) < 0) {
result = EBPF_INVALID_ARGUMENT;
goto Done;
}
Expand Down Expand Up @@ -1129,12 +1133,12 @@ _ebpf_native_resolve_helpers_for_program(
_In_ const ebpf_native_module_t* module, _In_ const ebpf_native_program_t* program)
{
EBPF_LOG_ENTRY();
UNREFERENCED_PARAMETER(module);
ebpf_result_t result;
uint32_t* helper_ids = NULL;
helper_function_address* helper_addresses = NULL;
helper_function_address_t* helper_addresses = NULL;
uint16_t helper_count = program->entry->helper_count;
helper_function_entry_t* helpers = program->entry->helpers;
bool implicit_context_supported = false;

if (helper_count > 0) {
helper_ids = ebpf_allocate_with_tag(helper_count * sizeof(uint32_t), EBPF_POOL_TAG_NATIVE);
Expand All @@ -1143,7 +1147,8 @@ _ebpf_native_resolve_helpers_for_program(
goto Done;
}

helper_addresses = ebpf_allocate_with_tag(helper_count * sizeof(helper_function_address), EBPF_POOL_TAG_NATIVE);
helper_addresses =
ebpf_allocate_with_tag(helper_count * sizeof(helper_function_address_t), EBPF_POOL_TAG_NATIVE);
if (helper_addresses == NULL) {
result = EBPF_NO_MEMORY;
goto Done;
Expand All @@ -1155,7 +1160,7 @@ _ebpf_native_resolve_helpers_for_program(
}
}

result = ebpf_core_resolve_helper(program->handle, helper_count, helper_ids, (uint64_t*)helper_addresses);
result = ebpf_core_resolve_helper(program->handle, helper_count, helper_ids, helper_addresses);
if (result != EBPF_SUCCESS) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
Expand All @@ -1165,9 +1170,23 @@ _ebpf_native_resolve_helpers_for_program(
goto Done;
}

if (_ebpf_compare_versions(&module->version, &_ebpf_version_implicit_context) >= 0) {
implicit_context_supported = true;
}

// Update the addresses in the helper entries.
for (uint16_t i = 0; i < helper_count; i++) {
helpers[i].address = helper_addresses[i];
if (!implicit_context_supported && helper_addresses[i].implicit_context) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_NATIVE,
"_ebpf_native_resolve_helpers_for_program: module does not support implicit context, but extension "
"does.",
&module->client_module_id);
result = EBPF_INVALID_ARGUMENT;
goto Done;
}
helpers[i].address = (helper_function_t)helper_addresses[i].address;
}

Done:
Expand Down Expand Up @@ -1718,14 +1737,16 @@ ebpf_native_get_count_of_maps(_In_ const GUID* module_id, _Out_ size_t* count_of

static ebpf_result_t
_ebpf_native_helper_address_changed(
size_t address_count, _In_reads_opt_(address_count) uintptr_t* addresses, _In_opt_ void* context)
size_t address_count, _In_reads_opt_(address_count) helper_function_address_t* addresses, _In_opt_ void* context)
{
ebpf_result_t return_value;
ebpf_native_helper_address_changed_context_t* helper_address_changed_context =
(ebpf_native_helper_address_changed_context_t*)context;
_Analysis_assume_(context != NULL);
ebpf_native_module_t* module = helper_address_changed_context->module;
bool implicit_context_supported = false;

uint64_t* helper_function_addresses = NULL;
_Analysis_assume_(context != NULL);
size_t helper_count = helper_address_changed_context->native_program->entry->helper_count;

if (helper_count == 0) {
Expand All @@ -1738,8 +1759,21 @@ _ebpf_native_helper_address_changed(
goto Done;
}

if (_ebpf_compare_versions(&module->version, &_ebpf_version_implicit_context) >= 0) {
implicit_context_supported = true;
}

for (size_t i = 0; i < helper_count; i++) {
*(uint64_t*)&(helper_address_changed_context->native_program->entry->helpers[i].address) = addresses[i];
if (!implicit_context_supported && addresses[i].implicit_context) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_NATIVE,
"_ebpf_native_helper_address_changed: module does not support implicit context, but extension does.",
&module->client_module_id);
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
*(uint64_t*)&(helper_address_changed_context->native_program->entry->helpers[i].address) = addresses[i].address;
}

return_value = EBPF_SUCCESS;
Expand Down
Loading
Loading