ebpf-for-windows/libs/execution_context/ebpf_program.c

1479 строки
52 KiB
C

// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
#include <stdlib.h>
#include "bpf_helpers.h"
#include "ebpf_core.h"
#include "ebpf_epoch.h"
#include "ebpf_handle.h"
#include "ebpf_link.h"
#include "ebpf_native.h"
#include "ebpf_object.h"
#include "ebpf_program.h"
#include "ebpf_program_attach_type_guids.h"
#include "ebpf_program_types.h"
#include "ebpf_state.h"
#include "ubpf.h"
static size_t _ebpf_program_state_index = MAXUINT64;
#define EBPF_MAX_HASH_SIZE 128
#define EBPF_HASH_ALGORITHM L"SHA256"
typedef struct _ebpf_program
{
ebpf_core_object_t object;
ebpf_program_parameters_t parameters;
// determinant is parameters.code_type
union
{
// EBPF_CODE_JIT
struct
{
ebpf_memory_descriptor_t* code_memory_descriptor;
uint8_t* code_pointer;
} code;
// EBPF_CODE_EBPF
struct ubpf_vm* vm;
// EBPF_CODE_NATIVE
struct
{
const ebpf_native_module_binding_context_t* module;
const uint8_t* code_pointer;
} native;
} code_or_vm;
ebpf_extension_client_t* general_helper_extension_client;
ebpf_extension_data_t* general_helper_provider_data;
ebpf_extension_dispatch_table_t* general_helper_provider_dispatch_table;
ebpf_extension_client_t* info_extension_client;
const void* info_extension_provider_binding_context;
const ebpf_extension_data_t* info_extension_provider_data;
// Program type specific helper function count.
uint32_t program_type_specific_helper_function_count;
// Global helper function count implemented by the extension.
uint32_t global_helper_function_count;
bool invalidated;
ebpf_trampoline_table_t* trampoline_table;
// Array of helper function ids referred by this program.
size_t helper_function_count;
uint32_t* helper_function_ids;
ebpf_epoch_work_item_t* cleanup_work_item;
// Lock protecting the fields below.
ebpf_lock_t lock;
ebpf_list_entry_t links;
uint32_t link_count;
ebpf_map_t** maps;
uint32_t count_of_maps;
} ebpf_program_t;
static ebpf_result_t
_ebpf_program_register_helpers(_In_ const ebpf_program_t* program);
static ebpf_result_t
_ebpf_program_get_helper_function_address(
_In_ const ebpf_program_t* program, const uint32_t helper_function_id, uint64_t* address);
_Must_inspect_result_ ebpf_result_t
ebpf_program_initiate()
{
return ebpf_state_allocate_index(&_ebpf_program_state_index);
}
void
ebpf_program_terminate()
{}
static void
_ebpf_program_detach_links(_Inout_ ebpf_program_t* program)
{
EBPF_LOG_ENTRY();
while (!ebpf_list_is_empty(&program->links)) {
ebpf_list_entry_t* entry = program->links.Flink;
ebpf_core_object_t* object = CONTAINING_RECORD(entry, ebpf_core_object_t, object_list_entry);
ebpf_link_detach_program((ebpf_link_t*)object);
}
EBPF_RETURN_VOID();
}
static ebpf_result_t
_ebpf_program_verify_program_info_hash(_In_ const ebpf_program_t* program);
static void
_ebpf_program_program_info_provider_changed(
_In_ const void* client_binding_context,
_In_ const void* provider_binding_context,
_In_opt_ const ebpf_extension_data_t* provider_data)
{
EBPF_LOG_ENTRY();
ebpf_result_t return_value;
ebpf_program_t* program = (ebpf_program_t*)client_binding_context;
uint32_t* total_helper_function_ids = NULL;
size_t total_helper_count = 0;
ebpf_helper_function_addresses_t* total_helper_function_addresses = NULL;
if (provider_data == NULL) {
// Extension is detaching. Program will get invalidated.
goto Exit;
} else {
ebpf_helper_function_addresses_t* helper_function_addresses = NULL;
ebpf_helper_function_addresses_t* global_helper_function_addresses = NULL;
ebpf_program_data_t* program_data = (ebpf_program_data_t*)provider_data->data;
if (program_data == NULL) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"An extension cannot have empty program_data",
program->parameters.program_type);
// An extension cannot have empty program_data.
goto Exit;
}
helper_function_addresses = program_data->program_type_specific_helper_function_addresses;
global_helper_function_addresses = program_data->global_helper_function_addresses;
if ((program->program_type_specific_helper_function_count > 0) &&
(helper_function_addresses->helper_function_count !=
program->program_type_specific_helper_function_count)) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"A program info provider cannot modify helper function count upon reload",
program->parameters.program_type);
// A program info provider cannot modify helper function count upon reload.
goto Exit;
}
if ((program->global_helper_function_count > 0) &&
(global_helper_function_addresses->helper_function_count != program->global_helper_function_count)) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"A program info provider cannot modify global helper function count upon reload",
program->parameters.program_type);
// A program info provider cannot modify helper function count upon reload.
goto Exit;
}
if (helper_function_addresses != NULL || global_helper_function_addresses != NULL) {
ebpf_program_info_t* program_info = program_data->program_info;
ebpf_helper_function_prototype_t* helper_prototypes = NULL;
ebpf_assert(program_info != NULL);
_Analysis_assume_(program_info != NULL);
if ((helper_function_addresses != NULL && program_info->count_of_program_type_specific_helpers !=
helper_function_addresses->helper_function_count) ||
(global_helper_function_addresses != NULL &&
program_info->count_of_global_helpers != global_helper_function_addresses->helper_function_count)) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"A program info provider cannot modify helper function count upon reload",
program->parameters.program_type);
return_value = EBPF_INVALID_ARGUMENT;
goto Exit;
}
program->program_type_specific_helper_function_count =
helper_function_addresses ? helper_function_addresses->helper_function_count : 0;
program->global_helper_function_count =
global_helper_function_addresses ? global_helper_function_addresses->helper_function_count : 0;
return_value = ebpf_safe_size_t_add(
program->program_type_specific_helper_function_count,
program->global_helper_function_count,
&total_helper_count);
if (return_value != EBPF_SUCCESS) {
goto Exit;
}
total_helper_function_addresses =
(ebpf_helper_function_addresses_t*)ebpf_allocate(sizeof(ebpf_helper_function_addresses_t));
if (total_helper_function_addresses == NULL) {
return_value = EBPF_NO_MEMORY;
goto Exit;
}
total_helper_function_addresses->helper_function_count = (uint32_t)total_helper_count;
total_helper_function_addresses->helper_function_address =
(uint64_t*)ebpf_allocate(sizeof(uint64_t) * total_helper_count);
if (total_helper_function_addresses->helper_function_address == NULL) {
return_value = EBPF_NO_MEMORY;
goto Exit;
}
if (!program->trampoline_table) {
// Program info provider is being loaded for the first time. Allocate trampoline table.
return_value = ebpf_allocate_trampoline_table(total_helper_count, &program->trampoline_table);
if (return_value != EBPF_SUCCESS)
goto Exit;
}
__analysis_assume(total_helper_count > 0);
total_helper_function_ids = (uint32_t*)ebpf_allocate(sizeof(uint32_t) * total_helper_count);
if (total_helper_function_ids == NULL) {
return_value = EBPF_NO_MEMORY;
goto Exit;
}
if (helper_function_addresses != NULL) {
helper_prototypes = program_info->program_type_specific_helper_prototype;
if (helper_prototypes == NULL) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"program_info->program_type_specific_helper_prototype can not be NULL",
program->parameters.program_type);
return_value = EBPF_INVALID_ARGUMENT;
goto Exit;
}
#pragma warning(push)
#pragma warning(disable : 6386) // Buffer overrun while writing to 'total_helper_function_ids'.
for (uint32_t index = 0; index < program->program_type_specific_helper_function_count; index++) {
total_helper_function_ids[index] = helper_prototypes[index].helper_id;
total_helper_function_addresses->helper_function_address[index] =
helper_function_addresses->helper_function_address[index];
}
}
#pragma warning(pop)
if (global_helper_function_addresses != NULL) {
helper_prototypes = program_info->global_helper_prototype;
if (helper_prototypes == NULL) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"program_info->global_helper_prototype can not be NULL",
program->parameters.program_type);
return_value = EBPF_INVALID_ARGUMENT;
goto Exit;
}
// TODO: Issue #1791: Check against a global allow / block list to see if these global
// helper functions can be overridden.
#pragma warning(push)
#pragma warning( \
disable : 6386) // Buffer overrun while writing to 'total_helper_function_addresses->helper_function_address'
for (uint32_t index = program->program_type_specific_helper_function_count; index < total_helper_count;
index++) {
uint32_t global_helper_index = index - program->program_type_specific_helper_function_count;
total_helper_function_ids[index] = helper_prototypes[global_helper_index].helper_id;
total_helper_function_addresses->helper_function_address[index] =
global_helper_function_addresses->helper_function_address[global_helper_index];
}
}
#pragma warning(pop)
return_value = ebpf_update_trampoline_table(
program->trampoline_table,
(uint32_t)total_helper_count,
total_helper_function_ids,
total_helper_function_addresses);
if (return_value != EBPF_SUCCESS)
goto Exit;
#if !defined(CONFIG_BPF_JIT_ALWAYS_ON)
if (program->code_or_vm.vm != NULL) {
// Register with uBPF for interpreted mode.
return_value = _ebpf_program_register_helpers(program);
if (return_value != EBPF_SUCCESS)
goto Exit;
}
#endif
}
}
program->info_extension_provider_binding_context = provider_binding_context;
program->info_extension_provider_data = provider_data;
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;
}
}
Exit:
ebpf_free(total_helper_function_ids);
if (total_helper_function_addresses != NULL) {
ebpf_free(total_helper_function_addresses->helper_function_address);
ebpf_free(total_helper_function_addresses);
}
program->invalidated = (program->info_extension_provider_data == NULL);
EBPF_RETURN_VOID();
}
/**
* @brief Free invoked by ebpf_core_object_t reference tracking. This schedules the
* final delete of the ebpf_program_t once the current epoch ends.
*
* @param[in] object Pointer to ebpf_core_object_t whose ref-count reached zero.
*/
static void
_ebpf_program_free(_In_opt_ _Post_invalid_ ebpf_core_object_t* object)
{
EBPF_LOG_ENTRY();
size_t index;
ebpf_program_t* program = (ebpf_program_t*)object;
if (!program)
EBPF_RETURN_VOID();
// Detach from all the attach points.
_ebpf_program_detach_links(program);
ebpf_assert(ebpf_list_is_empty(&program->links));
for (index = 0; index < program->count_of_maps; index++)
ebpf_object_release_reference((ebpf_core_object_t*)program->maps[index]);
ebpf_epoch_schedule_work_item(program->cleanup_work_item);
EBPF_RETURN_VOID();
}
static const ebpf_program_type_t*
_ebpf_program_get_program_type(_In_ const ebpf_core_object_t* object)
{
return ebpf_program_type_uuid((const ebpf_program_t*)object);
}
static const bpf_prog_type_t
_ebpf_program_get_bpf_prog_type(_In_ const ebpf_program_t* program)
{
bpf_prog_type_t prog_type = BPF_PROG_TYPE_UNSPEC;
if (program->info_extension_provider_binding_context != NULL) {
ebpf_program_data_t* program_data = (ebpf_program_data_t*)program->info_extension_provider_data->data;
prog_type = program_data->program_info->program_type_descriptor.bpf_prog_type;
}
return prog_type;
}
/**
* @brief Free invoked when the current epoch ends. Scheduled by
* _ebpf_program_free.
*
* @param[in] context Pointer to the ebpf_program_t passed as context in the
* work-item.
*/
static void
_ebpf_program_epoch_free(_In_ _Post_invalid_ void* context)
{
EBPF_LOG_ENTRY();
ebpf_program_t* program = (ebpf_program_t*)context;
ebpf_lock_destroy(&program->lock);
ebpf_extension_unload(program->general_helper_extension_client);
ebpf_extension_unload(program->info_extension_client);
switch (program->parameters.code_type) {
case EBPF_CODE_JIT:
ebpf_unmap_memory(program->code_or_vm.code.code_memory_descriptor);
break;
#if !defined(CONFIG_BPF_JIT_ALWAYS_ON)
case EBPF_CODE_EBPF:
if (program->code_or_vm.vm) {
ubpf_destroy(program->code_or_vm.vm);
}
break;
#endif
case EBPF_CODE_NATIVE:
ebpf_native_release_reference((ebpf_native_module_binding_context_t*)program->code_or_vm.native.module);
break;
case EBPF_CODE_NONE:
break;
}
ebpf_free(program->parameters.program_name.value);
ebpf_free(program->parameters.section_name.value);
ebpf_free(program->parameters.file_name.value);
ebpf_free((void*)program->parameters.program_info_hash);
ebpf_free(program->maps);
ebpf_free_trampoline_table(program->trampoline_table);
ebpf_free(program->helper_function_ids);
ebpf_free(program->cleanup_work_item);
ebpf_free(program);
EBPF_RETURN_VOID();
}
static ebpf_result_t
ebpf_program_load_providers(_Inout_ ebpf_program_t* program)
{
EBPF_LOG_ENTRY();
ebpf_result_t return_value;
void* provider_binding_context;
ebpf_program_data_t* general_helper_program_data = NULL;
GUID module_id = {0};
// First, register as a client of the general helper function
// provider and get the general helper program data.
return_value = ebpf_guid_create(&module_id);
if (return_value != EBPF_SUCCESS) {
goto Done;
}
program->invalidated = false;
return_value = ebpf_extension_load(
&program->general_helper_extension_client,
&ebpf_program_information_extension_interface_id, // Load program information extension.
&ebpf_general_helper_function_module_id, // Expected provider module Id.
&module_id,
program,
NULL,
NULL,
&provider_binding_context,
&program->general_helper_provider_data,
&program->general_helper_provider_dispatch_table,
NULL);
if (return_value != EBPF_SUCCESS) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"Failed to load general helper functions",
ebpf_general_helper_function_module_id);
goto Done;
}
if (program->general_helper_provider_data == NULL) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"program->general_helper_provider_data can not be NULL",
ebpf_general_helper_function_module_id);
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
general_helper_program_data = (ebpf_program_data_t*)program->general_helper_provider_data->data;
if (general_helper_program_data->program_type_specific_helper_function_addresses == NULL) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"general_helper_program_data->program_type_specific_helper_function_addresses can not be NULL",
ebpf_general_helper_function_module_id);
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
// Next, register as a client of the specific program type
// provider and get the data associated with that program type.
return_value = ebpf_guid_create(&module_id);
if (return_value != EBPF_SUCCESS) {
goto Done;
}
return_value = ebpf_extension_load(
&program->info_extension_client,
&ebpf_program_information_extension_interface_id, // Load program information extension.
&program->parameters.program_type, // Program type is the expected provider module Id.
&module_id,
program,
NULL,
NULL,
(void**)&program->info_extension_provider_binding_context,
&program->info_extension_provider_data,
NULL,
_ebpf_program_program_info_provider_changed);
if (return_value != EBPF_SUCCESS) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"Failed to load program information provider",
program->parameters.program_type);
goto Done;
}
Done:
EBPF_RETURN_RESULT(return_value);
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_create(_Outptr_ ebpf_program_t** program)
{
EBPF_LOG_ENTRY();
ebpf_result_t retval;
ebpf_program_t* local_program;
local_program = (ebpf_program_t*)ebpf_allocate(sizeof(ebpf_program_t));
if (!local_program) {
retval = EBPF_NO_MEMORY;
goto Done;
}
memset(local_program, 0, sizeof(ebpf_program_t));
local_program->cleanup_work_item = ebpf_epoch_allocate_work_item(local_program, _ebpf_program_epoch_free);
if (!local_program->cleanup_work_item) {
retval = EBPF_NO_MEMORY;
goto Done;
}
ebpf_list_initialize(&local_program->links);
ebpf_lock_create(&local_program->lock);
retval = ebpf_object_initialize(
&local_program->object, EBPF_OBJECT_PROGRAM, _ebpf_program_free, _ebpf_program_get_program_type);
if (retval != EBPF_SUCCESS) {
goto Done;
}
*program = local_program;
local_program = NULL;
retval = EBPF_SUCCESS;
Done:
if (local_program)
_ebpf_program_epoch_free(local_program);
EBPF_RETURN_RESULT(retval);
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_initialize(_Inout_ ebpf_program_t* program, _In_ const ebpf_program_parameters_t* program_parameters)
{
EBPF_LOG_ENTRY();
ebpf_result_t return_value;
ebpf_utf8_string_t local_program_name = {NULL, 0};
ebpf_utf8_string_t local_section_name = {NULL, 0};
ebpf_utf8_string_t local_file_name = {NULL, 0};
uint8_t* local_program_info_hash = NULL;
if (program->parameters.code_type != EBPF_CODE_NONE) {
EBPF_LOG_MESSAGE_UINT64(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"ebpf_program_initialize program->parameters.code_type must be EBPF_CODE_NONE",
program->parameters.code_type);
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,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"Program name must be less than BPF_OBJ_NAME_LEN",
program_parameters->program_name.length);
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
return_value = ebpf_duplicate_utf8_string(&local_program_name, &program_parameters->program_name);
if (return_value != EBPF_SUCCESS)
goto Done;
return_value = ebpf_duplicate_utf8_string(&local_section_name, &program_parameters->section_name);
if (return_value != EBPF_SUCCESS)
goto Done;
return_value = ebpf_duplicate_utf8_string(&local_file_name, &program_parameters->file_name);
if (return_value != EBPF_SUCCESS)
goto Done;
if (program_parameters->program_info_hash_length > 0) {
local_program_info_hash = ebpf_allocate(program_parameters->program_info_hash_length);
if (!local_program_info_hash) {
return_value = EBPF_NO_MEMORY;
goto Done;
}
memcpy(
local_program_info_hash,
program_parameters->program_info_hash,
program_parameters->program_info_hash_length);
}
program->parameters = *program_parameters;
program->parameters.program_name = local_program_name;
local_program_name.value = NULL;
program->parameters.section_name = local_section_name;
local_section_name.value = NULL;
program->parameters.file_name = local_file_name;
local_file_name.value = NULL;
program->parameters.code_type = EBPF_CODE_NONE;
program->parameters.program_info_hash = local_program_info_hash;
local_program_info_hash = NULL;
return_value = ebpf_program_load_providers(program);
if (return_value != EBPF_SUCCESS) {
goto Done;
}
return_value = EBPF_SUCCESS;
Done:
ebpf_free(local_program_info_hash);
ebpf_free(local_program_name.value);
ebpf_free(local_section_name.value);
ebpf_free(local_file_name.value);
EBPF_RETURN_RESULT(return_value);
}
_Ret_notnull_ const ebpf_program_parameters_t*
ebpf_program_get_parameters(_In_ const ebpf_program_t* program)
{
return &program->parameters;
}
_Ret_notnull_ const ebpf_program_type_t*
ebpf_program_type_uuid(_In_ const ebpf_program_t* program)
{
return &ebpf_program_get_parameters(program)->program_type;
}
_Ret_notnull_ const ebpf_attach_type_t*
ebpf_expected_attach_type(_In_ const ebpf_program_t* program)
{
return &ebpf_program_get_parameters(program)->expected_attach_type;
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_associate_additional_map(ebpf_program_t* program, ebpf_map_t* map)
{
EBPF_LOG_ENTRY();
// First make sure the map can be associated.
ebpf_result_t result = ebpf_map_associate_program(map, program);
if (result != EBPF_SUCCESS) {
EBPF_RETURN_RESULT(result);
}
ebpf_lock_state_t state = ebpf_lock_lock(&program->lock);
uint32_t map_count = program->count_of_maps + 1;
ebpf_map_t** program_maps =
ebpf_reallocate(program->maps, program->count_of_maps * sizeof(ebpf_map_t*), map_count * sizeof(ebpf_map_t*));
if (program_maps == NULL) {
result = EBPF_NO_MEMORY;
goto Done;
}
ebpf_object_acquire_reference((ebpf_core_object_t*)map);
program_maps[map_count - 1] = map;
program->maps = program_maps;
program->count_of_maps = map_count;
Done:
ebpf_lock_unlock(&program->lock, state);
EBPF_RETURN_RESULT(result);
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_associate_maps(ebpf_program_t* program, ebpf_map_t** maps, uint32_t maps_count)
{
EBPF_LOG_ENTRY();
size_t index;
ebpf_map_t** program_maps = ebpf_allocate(maps_count * sizeof(ebpf_map_t*));
if (!program_maps)
return EBPF_NO_MEMORY;
memcpy(program_maps, maps, sizeof(ebpf_map_t*) * maps_count);
// Before we acquire any references, make sure
// all maps can be associated.
ebpf_result_t result = EBPF_SUCCESS;
for (index = 0; index < maps_count; index++) {
ebpf_map_t* map = program_maps[index];
result = ebpf_map_associate_program(map, program);
if (result != EBPF_SUCCESS) {
ebpf_free(program_maps);
EBPF_RETURN_RESULT(result);
}
}
// Now go through again and acquire references.
program->maps = program_maps;
program->count_of_maps = maps_count;
for (index = 0; index < maps_count; index++) {
ebpf_object_acquire_reference((ebpf_core_object_t*)program_maps[index]);
}
EBPF_RETURN_RESULT(EBPF_SUCCESS);
}
static ebpf_result_t
_ebpf_program_load_machine_code(
_Inout_ ebpf_program_t* program,
_In_opt_ const void* code_context,
_In_reads_(machine_code_size) const uint8_t* machine_code,
size_t machine_code_size)
{
EBPF_LOG_ENTRY();
ebpf_result_t return_value;
uint8_t* local_machine_code = NULL;
ebpf_memory_descriptor_t* local_code_memory_descriptor = NULL;
ebpf_assert(program->parameters.code_type == EBPF_CODE_JIT || program->parameters.code_type == EBPF_CODE_NATIVE);
if (program->parameters.code_type == EBPF_CODE_JIT) {
local_code_memory_descriptor = ebpf_map_memory(machine_code_size);
if (!local_code_memory_descriptor) {
return_value = EBPF_NO_MEMORY;
goto Done;
}
local_machine_code = ebpf_memory_descriptor_get_base_address(local_code_memory_descriptor);
memcpy(local_machine_code, machine_code, machine_code_size);
return_value = ebpf_protect_memory(local_code_memory_descriptor, EBPF_PAGE_PROTECT_READ_EXECUTE);
if (return_value != EBPF_SUCCESS) {
goto Done;
}
program->code_or_vm.code.code_memory_descriptor = local_code_memory_descriptor;
program->code_or_vm.code.code_pointer = local_machine_code;
local_code_memory_descriptor = NULL;
} else {
ebpf_assert(machine_code_size == 0);
if (code_context == NULL) {
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
program->code_or_vm.native.module = code_context;
program->code_or_vm.native.code_pointer = machine_code;
// Acquire reference on the native module. This reference
// will be released when the ebpf_program is freed.
ebpf_native_acquire_reference((ebpf_native_module_binding_context_t*)code_context);
}
return_value = EBPF_SUCCESS;
Done:
ebpf_unmap_memory(local_code_memory_descriptor);
EBPF_RETURN_RESULT(return_value);
}
static ebpf_result_t
_ebpf_program_register_helpers(_In_ const ebpf_program_t* program)
{
EBPF_LOG_ENTRY();
ebpf_result_t result = EBPF_SUCCESS;
size_t index = 0;
ebpf_assert(program->code_or_vm.vm != NULL);
for (index = 0; index < program->helper_function_count; index++) {
uint32_t helper_function_id = program->helper_function_ids[index];
void* helper = NULL;
result = _ebpf_program_get_helper_function_address(program, helper_function_id, (uint64_t*)&helper);
if (result != EBPF_SUCCESS) {
goto Exit;
}
if (helper == NULL)
continue;
#if !defined(CONFIG_BPF_JIT_ALWAYS_ON)
if (ubpf_register(program->code_or_vm.vm, (unsigned int)index, NULL, (void*)helper) < 0) {
EBPF_LOG_MESSAGE_UINT64(
EBPF_TRACELOG_LEVEL_ERROR, EBPF_TRACELOG_KEYWORD_PROGRAM, "ubpf_register failed", index);
result = EBPF_INVALID_ARGUMENT;
goto Exit;
}
#endif
}
Exit:
EBPF_RETURN_RESULT(result);
}
#if !defined(CONFIG_BPF_JIT_ALWAYS_ON)
static ebpf_result_t
_ebpf_program_load_byte_code(
_Inout_ ebpf_program_t* program, _In_ const ebpf_instruction_t* instructions, size_t instruction_count)
{
EBPF_LOG_ENTRY();
ebpf_result_t return_value;
char* error_message = NULL;
if (program->parameters.code_type != EBPF_CODE_EBPF) {
EBPF_LOG_MESSAGE_UINT64(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"_ebpf_program_load_byte_code program->parameters.code_type must be EBPF_CODE_EBPF",
program->parameters.code_type);
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
// ubpf currently requires the byte count to fit in a uint32_t.
if (instruction_count > UINT32_MAX / sizeof(ebpf_instruction_t)) {
return_value = EBPF_PROGRAM_TOO_LARGE;
goto Done;
}
program->code_or_vm.vm = ubpf_create();
if (!program->code_or_vm.vm) {
return_value = EBPF_NO_MEMORY;
goto Done;
}
// https://github.com/iovisor/ubpf/issues/68
// BUG - ubpf implements bounds checking to detect interpreted code accessing
// memory out of bounds. Currently this is flagging valid access checks and
// failing.
ubpf_toggle_bounds_check(program->code_or_vm.vm, false);
ubpf_set_error_print(program->code_or_vm.vm, ebpf_log_function);
return_value = _ebpf_program_register_helpers(program);
if (return_value != EBPF_SUCCESS)
goto Done;
if (ubpf_load(
program->code_or_vm.vm,
instructions,
(uint32_t)(instruction_count * sizeof(ebpf_instruction_t)),
&error_message) != 0) {
EBPF_LOG_MESSAGE_STRING(
EBPF_TRACELOG_LEVEL_ERROR, EBPF_TRACELOG_KEYWORD_PROGRAM, "ubpf_load failed", error_message);
ebpf_free(error_message);
return_value = EBPF_INVALID_ARGUMENT;
goto Done;
}
Done:
if (return_value != EBPF_SUCCESS) {
if (program->code_or_vm.vm)
ubpf_destroy(program->code_or_vm.vm);
program->code_or_vm.vm = NULL;
}
EBPF_RETURN_RESULT(return_value);
}
#endif
_Must_inspect_result_ ebpf_result_t
ebpf_program_load_code(
_Inout_ ebpf_program_t* program,
ebpf_code_type_t code_type,
_In_opt_ const void* code_context,
_In_reads_(code_size) const uint8_t* code,
size_t code_size)
{
EBPF_LOG_ENTRY();
ebpf_result_t result = EBPF_SUCCESS;
program->parameters.code_type = code_type;
ebpf_assert(
(code_type == EBPF_CODE_NATIVE && code_context != NULL) ||
(code_type != EBPF_CODE_NATIVE && code_context == NULL));
if (program->parameters.code_type == EBPF_CODE_JIT || program->parameters.code_type == EBPF_CODE_NATIVE)
result = _ebpf_program_load_machine_code(program, code_context, code, code_size);
else if (program->parameters.code_type == EBPF_CODE_EBPF)
#if !defined(CONFIG_BPF_JIT_ALWAYS_ON)
result = _ebpf_program_load_byte_code(
program, (const ebpf_instruction_t*)code, code_size / sizeof(ebpf_instruction_t));
#else
result = EBPF_BLOCKED_BY_POLICY;
#endif
else {
EBPF_LOG_MESSAGE_UINT64(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"ebpf_program_load_code unknown program->parameters.code_type",
program->parameters.code_type);
result = EBPF_INVALID_ARGUMENT;
}
EBPF_RETURN_RESULT(result);
}
typedef struct _ebpf_program_tail_call_state
{
const ebpf_program_t* next_program;
uint32_t count;
} ebpf_program_tail_call_state_t;
_Must_inspect_result_ ebpf_result_t
ebpf_program_set_tail_call(_In_ const ebpf_program_t* next_program)
{
// High volume call - Skip entry/exit logging.
ebpf_result_t result;
ebpf_program_tail_call_state_t* state = NULL;
result = ebpf_state_load(_ebpf_program_state_index, (uintptr_t*)&state);
if (result != EBPF_SUCCESS)
return result;
if (state == NULL)
return EBPF_INVALID_ARGUMENT;
if (state->count == MAX_TAIL_CALL_CNT) {
return EBPF_NO_MORE_TAIL_CALLS;
}
state->next_program = next_program;
return EBPF_SUCCESS;
}
void
ebpf_program_invoke(_In_ const ebpf_program_t* program, _Inout_ void* context, _Out_ uint32_t* result)
{
// High volume call - Skip entry/exit logging.
ebpf_program_tail_call_state_t state = {0};
const ebpf_program_t* current_program = program;
if (!program || program->invalidated) {
*result = 0;
return;
}
if (!ebpf_state_store(_ebpf_program_state_index, (uintptr_t)&state) == EBPF_SUCCESS) {
*result = 0;
return;
}
for (state.count = 0; state.count < MAX_TAIL_CALL_CNT; state.count++) {
if (current_program->parameters.code_type == EBPF_CODE_JIT ||
current_program->parameters.code_type == EBPF_CODE_NATIVE) {
ebpf_program_entry_point_t function_pointer;
function_pointer = (ebpf_program_entry_point_t)(current_program->code_or_vm.code.code_pointer);
*result = (function_pointer)(context);
} else {
#if !defined(CONFIG_BPF_JIT_ALWAYS_ON)
uint64_t out_value;
int ret = (uint32_t)(ubpf_exec(current_program->code_or_vm.vm, context, 1024, &out_value));
if (ret < 0) {
*result = ret;
} else {
*result = (uint32_t)(out_value);
}
#else
*result = 0;
#endif
}
if (state.count != 0) {
ebpf_object_release_reference((ebpf_core_object_t*)current_program);
current_program = NULL;
}
if (state.next_program == NULL) {
break;
} else {
current_program = state.next_program;
state.next_program = NULL;
}
}
ebpf_assert_success(ebpf_state_store(_ebpf_program_state_index, 0));
}
static ebpf_result_t
_ebpf_program_get_helper_function_address(
_In_ const ebpf_program_t* program, const uint32_t helper_function_id, uint64_t* address)
{
ebpf_result_t return_value;
void* function_address;
EBPF_LOG_ENTRY();
if (helper_function_id > EBPF_MAX_GENERAL_HELPER_FUNCTION) {
if (!program->trampoline_table) {
EBPF_RETURN_RESULT(EBPF_INVALID_ARGUMENT);
}
return_value = ebpf_get_trampoline_function(program->trampoline_table, helper_function_id, &function_address);
if (return_value != EBPF_SUCCESS)
EBPF_RETURN_RESULT(return_value);
*address = (uint64_t)function_address;
} else {
// First check if the extension has provided a new implementation of
// the global helper function.
if (program->trampoline_table) {
return_value =
ebpf_get_trampoline_function(program->trampoline_table, helper_function_id, &function_address);
if (return_value == EBPF_SUCCESS) {
// Found an entry.
*address = (uint64_t)function_address;
EBPF_RETURN_RESULT(EBPF_SUCCESS);
}
}
ebpf_assert(program->general_helper_provider_data != NULL);
_Analysis_assume_(program->general_helper_provider_data != NULL);
ebpf_program_data_t* general_helper_program_data =
(ebpf_program_data_t*)program->general_helper_provider_data->data;
ebpf_assert(general_helper_program_data != NULL);
_Analysis_assume_(general_helper_program_data != NULL);
ebpf_helper_function_addresses_t* general_helper_function_addresses =
general_helper_program_data->program_type_specific_helper_function_addresses;
ebpf_assert(general_helper_function_addresses != NULL);
_Analysis_assume_(general_helper_function_addresses != NULL);
if (helper_function_id > general_helper_function_addresses->helper_function_count) {
return EBPF_INVALID_ARGUMENT;
}
*address = general_helper_function_addresses->helper_function_address[helper_function_id];
}
EBPF_RETURN_RESULT(EBPF_SUCCESS);
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_get_helper_function_addresses(
_In_ const ebpf_program_t* program, size_t addresses_count, _Out_writes_(addresses_count) uint64_t* addresses)
{
EBPF_LOG_ENTRY();
ebpf_result_t result = EBPF_SUCCESS;
if (program->helper_function_count > addresses_count) {
result = EBPF_INSUFFICIENT_BUFFER;
goto Exit;
}
for (uint32_t index = 0; index < program->helper_function_count; index++) {
result =
_ebpf_program_get_helper_function_address(program, program->helper_function_ids[index], &addresses[index]);
if (result != EBPF_SUCCESS)
break;
}
Exit:
EBPF_RETURN_RESULT(result);
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_set_helper_function_ids(
_Inout_ ebpf_program_t* program,
const size_t helper_function_count,
_In_reads_(helper_function_count) const uint32_t* helper_function_ids)
{
EBPF_LOG_ENTRY();
ebpf_result_t result = EBPF_SUCCESS;
if (program->helper_function_ids != NULL) {
EBPF_LOG_MESSAGE(
EBPF_TRACELOG_LEVEL_ERROR,
EBPF_TRACELOG_KEYWORD_PROGRAM,
"ebpf_program_set_helper_function_ids - helper function IDs already set");
// Helper function IDs already set.
result = EBPF_INVALID_ARGUMENT;
goto Exit;
}
if (helper_function_count == 0)
goto Exit;
program->helper_function_count = helper_function_count;
program->helper_function_ids = ebpf_allocate(sizeof(uint32_t) * helper_function_count);
if (program->helper_function_ids == NULL) {
result = EBPF_NO_MEMORY;
goto Exit;
}
for (size_t index = 0; index < helper_function_count; index++)
program->helper_function_ids[index] = helper_function_ids[index];
Exit:
EBPF_RETURN_RESULT(result);
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_get_program_info(_In_ const ebpf_program_t* program, _Outptr_ ebpf_program_info_t** program_info)
{
EBPF_LOG_ENTRY();
ebpf_result_t result = EBPF_SUCCESS;
ebpf_program_data_t* program_data = NULL;
ebpf_program_data_t* general_helper_program_data = NULL;
ebpf_program_info_t* local_program_info = NULL;
uint32_t total_count_of_helpers = 0;
uint32_t helper_index = 0;
ebpf_assert(program_info);
*program_info = NULL;
if (program->invalidated) {
result = EBPF_EXTENSION_FAILED_TO_LOAD;
goto Exit;
}
if (!program->info_extension_provider_data) {
result = EBPF_EXTENSION_FAILED_TO_LOAD;
goto Exit;
}
program_data = (ebpf_program_data_t*)program->info_extension_provider_data->data;
if (!program->general_helper_provider_data) {
result = EBPF_EXTENSION_FAILED_TO_LOAD;
goto Exit;
}
general_helper_program_data = (ebpf_program_data_t*)program->general_helper_provider_data->data;
total_count_of_helpers = program_data->program_info->count_of_program_type_specific_helpers +
general_helper_program_data->program_info->count_of_program_type_specific_helpers;
if ((total_count_of_helpers < program_data->program_info->count_of_program_type_specific_helpers) ||
(total_count_of_helpers < general_helper_program_data->program_info->count_of_program_type_specific_helpers)) {
result = EBPF_ARITHMETIC_OVERFLOW;
goto Exit;
}
// Allocate buffer and make a shallow copy of the program info.
local_program_info = (ebpf_program_info_t*)ebpf_allocate(sizeof(ebpf_program_info_t));
if (local_program_info == NULL) {
result = EBPF_NO_MEMORY;
goto Exit;
}
local_program_info->program_type_descriptor = program_data->program_info->program_type_descriptor;
local_program_info->count_of_program_type_specific_helpers = total_count_of_helpers;
if (total_count_of_helpers > 0) {
// Allocate buffer and make a shallow copy of the combined global and program-type specific helper function
// prototypes.
local_program_info->program_type_specific_helper_prototype = (ebpf_helper_function_prototype_t*)ebpf_allocate(
total_count_of_helpers * sizeof(ebpf_helper_function_prototype_t));
if (local_program_info->program_type_specific_helper_prototype == NULL) {
result = EBPF_NO_MEMORY;
goto Exit;
}
for (uint32_t index = 0; index < program_data->program_info->count_of_program_type_specific_helpers; index++) {
__analysis_assume(helper_index < total_count_of_helpers);
local_program_info->program_type_specific_helper_prototype[helper_index++] =
program_data->program_info->program_type_specific_helper_prototype[index];
}
for (uint32_t index = 0;
index < general_helper_program_data->program_info->count_of_program_type_specific_helpers;
index++) {
__analysis_assume(helper_index < total_count_of_helpers);
local_program_info->program_type_specific_helper_prototype[helper_index++] =
general_helper_program_data->program_info->program_type_specific_helper_prototype[index];
}
}
Exit:
if (result == EBPF_SUCCESS) {
*program_info = local_program_info;
local_program_info = NULL;
} else {
ebpf_program_free_program_info(local_program_info);
}
EBPF_RETURN_RESULT(result);
}
void
ebpf_program_free_program_info(_In_opt_ _Post_invalid_ ebpf_program_info_t* program_info)
{
if (program_info != NULL) {
ebpf_free(program_info->program_type_specific_helper_prototype);
ebpf_free(program_info->global_helper_prototype);
ebpf_free(program_info);
}
}
void
ebpf_program_attach_link(_Inout_ ebpf_program_t* program, _Inout_ ebpf_link_t* link)
{
EBPF_LOG_ENTRY();
// Acquire "attach" reference on the link object.
ebpf_object_acquire_reference((ebpf_core_object_t*)link);
// Insert the link in the attach list.
ebpf_lock_state_t state;
state = ebpf_lock_lock(&program->lock);
ebpf_list_insert_tail(&program->links, &((ebpf_core_object_t*)link)->object_list_entry);
program->link_count++;
ebpf_lock_unlock(&program->lock, state);
EBPF_RETURN_VOID();
}
void
ebpf_program_detach_link(_Inout_ ebpf_program_t* program, _Inout_ ebpf_link_t* link)
{
EBPF_LOG_ENTRY();
// Remove the link from the attach list.
ebpf_lock_state_t state;
state = ebpf_lock_lock(&program->lock);
ebpf_list_remove_entry(&((ebpf_core_object_t*)link)->object_list_entry);
program->link_count--;
ebpf_lock_unlock(&program->lock, state);
// Release the "attach" reference.
ebpf_object_release_reference((ebpf_core_object_t*)link);
EBPF_RETURN_VOID();
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_get_info(
_In_ const ebpf_program_t* program,
_In_reads_(*info_size) const uint8_t* input_buffer,
_Out_writes_to_(*info_size, *info_size) uint8_t* output_buffer,
_Inout_ uint16_t* info_size)
{
EBPF_LOG_ENTRY();
const struct bpf_prog_info* input_info = (const struct bpf_prog_info*)input_buffer;
struct bpf_prog_info* output_info = (struct bpf_prog_info*)output_buffer;
if (*info_size < sizeof(*output_info)) {
EBPF_RETURN_RESULT(EBPF_INSUFFICIENT_BUFFER);
}
ebpf_result_t result = EBPF_SUCCESS;
ebpf_id_t* map_ids = (ebpf_id_t*)input_info->map_ids;
if ((input_info->map_ids != 0) && (input_info->nr_map_ids > 0) && (program->count_of_maps > 0)) {
// Fill in map ids before we overwrite the info buffer.
uint32_t max_nr_map_ids = input_info->nr_map_ids;
size_t length = max_nr_map_ids * sizeof(ebpf_id_t);
__try {
ebpf_probe_for_write(map_ids, length, sizeof(ebpf_id_t));
for (uint32_t i = 0; i < program->count_of_maps; i++) {
if (i == max_nr_map_ids) {
// No more space left.
EBPF_RETURN_RESULT(EBPF_INVALID_POINTER);
} else {
ebpf_map_t* map = program->maps[i];
map_ids[i] = ebpf_map_get_id(map);
}
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
EBPF_RETURN_RESULT(EBPF_INVALID_POINTER);
}
}
memset(output_info, 0, sizeof(*output_info));
output_info->id = program->object.id;
strncpy_s(
output_info->name,
sizeof(output_info->name),
(char*)program->parameters.program_name.value,
program->parameters.program_name.length);
output_info->nr_map_ids = program->count_of_maps;
output_info->map_ids = (uintptr_t)map_ids;
output_info->type = _ebpf_program_get_bpf_prog_type(program);
output_info->type_uuid = *ebpf_program_type_uuid(program);
output_info->attach_type_uuid = *ebpf_expected_attach_type(program);
output_info->pinned_path_count = program->object.pinned_path_count;
output_info->link_count = program->link_count;
*info_size = sizeof(*output_info);
EBPF_RETURN_RESULT(result);
}
_Must_inspect_result_ ebpf_result_t
ebpf_program_create_and_initialize(
_In_ const ebpf_program_parameters_t* parameters, _Out_ ebpf_handle_t* program_handle)
{
ebpf_result_t retval;
ebpf_program_t* program = NULL;
retval = ebpf_program_create(&program);
if (retval != EBPF_SUCCESS)
goto Done;
retval = ebpf_program_initialize(program, parameters);
if (retval != EBPF_SUCCESS)
goto Done;
retval = ebpf_handle_create(program_handle, (ebpf_base_object_t*)program);
if (retval != EBPF_SUCCESS)
goto Done;
Done:
ebpf_object_release_reference((ebpf_core_object_t*)program);
return retval;
}
typedef struct _ebpf_helper_id_to_index
{
uint32_t helper_id;
uint32_t index;
} ebpf_helper_id_to_index_t;
int
_ebpf_helper_id_to_index_compare(const void* lhs, const void* rhs)
{
const ebpf_helper_id_to_index_t* left = (const ebpf_helper_id_to_index_t*)lhs;
const ebpf_helper_id_to_index_t* right = (const ebpf_helper_id_to_index_t*)rhs;
return (left->helper_id < right->helper_id) ? -1 : (left->helper_id > right->helper_id) ? 1 : 0;
}
/**
* @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.
*
* @param[in] program Program to validate.
* @param[in] program_info Program info to validate against.
* @return EBPF_SUCCESS the program info hash matches.
* @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_LOG_ENTRY();
ebpf_result_t result;
ebpf_cryptographic_hash_t* cryptographic_hash = NULL;
ebpf_helper_id_to_index_t* helper_id_to_index = NULL;
const ebpf_program_info_t* program_info = NULL;
result = ebpf_program_get_program_info(program, &program_info);
if (result != EBPF_SUCCESS) {
goto Exit;
}
helper_id_to_index = (ebpf_helper_id_to_index_t*)ebpf_allocate(
program_info->count_of_program_type_specific_helpers * sizeof(ebpf_helper_id_to_index_t));
if (helper_id_to_index == NULL) {
result = EBPF_NO_MEMORY;
goto Exit;
}
for (uint32_t index = 0; index < program_info->count_of_program_type_specific_helpers; index++) {
helper_id_to_index[index].helper_id = program_info->program_type_specific_helper_prototype[index].helper_id;
helper_id_to_index[index].index = index;
}
// Sort helper_id_to_index by helper_id.
qsort(
helper_id_to_index,
program_info->count_of_program_type_specific_helpers,
sizeof(ebpf_helper_id_to_index_t),
_ebpf_helper_id_to_index_compare);
result = ebpf_cryptographic_hash_create(EBPF_HASH_ALGORITHM, &cryptographic_hash);
if (result != EBPF_SUCCESS) {
goto Exit;
}
// Hash is performed over the following fields:
// 1. Program type name.
// 2. Context descriptor.
// 3. Program type.
// 4. BPF program type.
// 5. Is_privileged flag.
// 6. Count of helpers.
// 7. For each program type specific helper (in helper id order).
// a. Helper id.
// b. Helper name.
// c. Helper return type.
// d. Helper argument types.
// Note:
// Order and fields being hashed is important. The order and fields being hashed must match the order and fields
// being hashed in bpf2c. If new fields are added to the program info, then the hash must be updated to include the
// new fields, both here and in bpf2c.
result = EBPF_CRYPTOGRAPHIC_HASH_APPEND_STR(cryptographic_hash, program_info->program_type_descriptor.name);
if (result != EBPF_SUCCESS) {
goto Exit;
}
result = EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(
cryptographic_hash, *program_info->program_type_descriptor.context_descriptor);
if (result != EBPF_SUCCESS) {
goto Exit;
}
result =
EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(cryptographic_hash, program_info->program_type_descriptor.program_type);
if (result != EBPF_SUCCESS) {
goto Exit;
}
result =
EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(cryptographic_hash, program_info->program_type_descriptor.bpf_prog_type);
if (result != EBPF_SUCCESS) {
goto Exit;
}
result =
EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(cryptographic_hash, program_info->program_type_descriptor.is_privileged);
if (result != EBPF_SUCCESS) {
goto Exit;
}
result =
EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(cryptographic_hash, program_info->count_of_program_type_specific_helpers);
if (result != EBPF_SUCCESS) {
goto Exit;
}
for (uint32_t i = 0; i < program_info->count_of_program_type_specific_helpers; i++) {
uint32_t index = helper_id_to_index[i].index;
result = EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(
cryptographic_hash, program_info->program_type_specific_helper_prototype[index].helper_id);
if (result != EBPF_SUCCESS) {
goto Exit;
}
result = EBPF_CRYPTOGRAPHIC_HASH_APPEND_STR(
cryptographic_hash, program_info->program_type_specific_helper_prototype[index].name);
if (result != EBPF_SUCCESS) {
goto Exit;
}
result = EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(
cryptographic_hash, program_info->program_type_specific_helper_prototype[index].return_type);
if (result != EBPF_SUCCESS) {
goto Exit;
}
for (uint32_t j = 0; j < EBPF_COUNT_OF(program_info->program_type_specific_helper_prototype[index].arguments);
j++) {
result = EBPF_CRYPTOGRAPHIC_HASH_APPEND_VALUE(
cryptographic_hash, program_info->program_type_specific_helper_prototype[index].arguments[j]);
if (result != EBPF_SUCCESS) {
goto Exit;
}
}
}
uint8_t hash[EBPF_MAX_HASH_SIZE];
size_t hash_length = EBPF_MAX_HASH_SIZE;
size_t output_hash_length = 0;
result = ebpf_cryptographic_hash_get_hash(cryptographic_hash, hash, hash_length, &output_hash_length);
if (result != EBPF_SUCCESS) {
goto Exit;
}
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;
}
result = EBPF_SUCCESS;
Exit:
ebpf_free(helper_id_to_index);
ebpf_cryptographic_hash_destroy(cryptographic_hash);
ebpf_program_free_program_info((ebpf_program_info_t*)program_info);
EBPF_RETURN_RESULT(result);
}