diff --git a/libs/execution_context/ebpf_native.c b/libs/execution_context/ebpf_native.c index 3df6a05ae1..38ef28b55e 100644 --- a/libs/execution_context/ebpf_native.c +++ b/libs/execution_context/ebpf_native.c @@ -276,6 +276,11 @@ _ebpf_validate_native_btf_resolved_function_entry_array( return false; } + // Validate the first element before using its "total_size" as the array stride. + if (!_ebpf_validate_native_btf_resolved_function_entry(native_btf_entry_array)) { + return false; + } + size_t entry_size = native_btf_entry_array[0].header.total_size; for (uint16_t i = 0; i < count; i++) { const btf_resolved_function_entry_t* native_btf_entry = @@ -309,6 +314,10 @@ _ebpf_validate_native_helper_function_entry_array( if (native_helper_function_entry_array == NULL) { return false; } + // Validate the first element before using its "total_size" as the array stride. + if (!_ebpf_validate_native_helper_function_entry(native_helper_function_entry_array)) { + return false; + } // Use "total_size" to calculate the actual size of the helper_function_entry_t struct. size_t helper_prototype_size = native_helper_function_entry_array[0].header.total_size; for (uint16_t i = 0; i < count; i++) { @@ -353,6 +362,10 @@ _ebpf_validate_native_program_entry_array( if (native_program_entry_array == NULL) { return false; } + // Validate the first element before using its "total_size" as the array stride. + if (!_ebpf_validate_native_program_entry(native_program_entry_array)) { + return false; + } // Use "total_size" to calculate the actual size of the program_entry_t struct. size_t program_entry_size = native_program_entry_array[0].header.total_size; for (size_t i = 0; i < count; i++) { @@ -384,6 +397,10 @@ _ebpf_validate_native_map_entry_array(_In_reads_(count) const map_entry_t* nativ if (native_map_entry_array == NULL) { return false; } + // Validate the first element before using its "total_size" as the array stride. + if (!_ebpf_validate_native_map_entry(native_map_entry_array)) { + return false; + } // Use "total_size" to calculate the actual size of the map_entry_t struct. size_t map_entry_size = native_map_entry_array[0].header.total_size; for (size_t i = 0; i < count; i++) { @@ -417,6 +434,10 @@ _ebpf_validate_native_map_initial_values_array( if (native_map_initial_values_array == NULL) { return false; } + // Validate the first element before using its "total_size" as the array stride. + if (!_ebpf_validate_native_map_initial_values(native_map_initial_values_array)) { + return false; + } // Use "total_size" to calculate the actual size of the map_initial_values_t struct. size_t map_initial_values_size = native_map_initial_values_array[0].header.total_size; for (size_t i = 0; i < count; i++) { @@ -451,6 +472,10 @@ _ebpf_validate_global_variable_section_info_array( if (native_global_variable_section_info_array == NULL) { return false; } + // Validate the first element before using its "total_size" as the array stride. + if (!_ebpf_validate_native_global_variable_section_info(native_global_variable_section_info_array)) { + return false; + } // Use "total_size" to calculate the actual size of the global_variable_section_info_t struct. size_t global_variable_section_info_size = native_global_variable_section_info_array[0].header.total_size; for (size_t i = 0; i < count; i++) { @@ -1256,7 +1281,7 @@ _ebpf_native_initialize_maps( for (uint32_t i = 0; i < map_count; i++) { // Copy the map_entry_t from native module to ebpf_native_map_t. map_entry_t* map_entry = (map_entry_t*)ARRAY_ELEMENT_INDEX(maps, i, map_entry_size); - memcpy(&native_maps[i].entry, map_entry, map_entry_size); + memcpy(&native_maps[i].entry, map_entry, min(map_entry_size, sizeof(native_maps[i].entry))); map_entry_t* entry = &native_maps[i].entry; if (entry->definition.pinning != LIBBPF_PIN_NONE && entry->definition.pinning != LIBBPF_PIN_BY_NAME) { @@ -1555,7 +1580,10 @@ _ebpf_native_initialize_global_variables( global_variables, i, global_variable_section_info_size); // Copy the global variable section info. - memcpy(&local_global_section_info, global_variable_section_info, global_variable_section_info_size); + memcpy( + &local_global_section_info, + global_variable_section_info, + min(global_variable_section_info_size, sizeof(local_global_section_info))); global_variable_section_info = NULL; const ebpf_native_map_t* native_map = _ebpf_native_find_map_by_name(instance, local_global_section_info.name); @@ -1825,7 +1853,7 @@ _ebpf_native_resolve_helpers_for_program( helper_function_entry_t local_helper_entry = {0}; const helper_function_entry_t* entry = (const helper_function_entry_t*)ARRAY_ELEMENT_INDEX(helper_info, i, helper_entry_size); - memcpy(&local_helper_entry, entry, helper_entry_size); + memcpy(&local_helper_entry, entry, min(helper_entry_size, sizeof(local_helper_entry))); if (local_helper_entry.helper_id == 0) { // Sentinel entry — this helper is not used by this program. @@ -1977,7 +2005,10 @@ _ebpf_native_initialize_programs(_Inout_ ebpf_native_module_instance_t* instance for (uint32_t i = 0; i < native_program->program_entry.helper_count; i++) { const helper_function_entry_t* helper_entry = (const helper_function_entry_t*)ARRAY_ELEMENT_INDEX(helper_info, i, helper_entry_size); - memcpy(&native_program->program_entry.helpers[i], helper_entry, helper_entry_size); + memcpy( + &native_program->program_entry.helpers[i], + helper_entry, + min(helper_entry_size, sizeof(native_program->program_entry.helpers[i]))); helper_entry = NULL; } } @@ -2717,7 +2748,7 @@ _ebpf_native_helper_address_changed( helper_function_entry_t local_helper_entry = {0}; const helper_function_entry_t* entry = (const helper_function_entry_t*)ARRAY_ELEMENT_INDEX(helper_info, i, helper_entry_size); - memcpy(&local_helper_entry, entry, helper_entry_size); + memcpy(&local_helper_entry, entry, min(helper_entry_size, sizeof(local_helper_entry))); if (local_helper_entry.helper_id == 0) { // Sentinel entry — skip. diff --git a/libs/shared/shared_common.c b/libs/shared/shared_common.c index e74aff581a..eade7adfad 100644 --- a/libs/shared/shared_common.c +++ b/libs/shared/shared_common.c @@ -68,6 +68,37 @@ uint16_t _supported_ebpf_extension_version[] = { EBPF_NATIVE_GLOBAL_VARIABLE_SECTION_DATA_CURRENT_VERSION, }; +// Maximum "total_size" for each extension object type. An object's total_size covers the structure including any +// trailing padding, so it can never exceed the size of the current version of that structure. +size_t _ebpf_extension_type_max_total_size[] = { + // eBPF extension object maximum total sizes. + EBPF_ATTACH_PROVIDER_DATA_CURRENT_VERSION_TOTAL_SIZE, + EBPF_PROGRAM_TYPE_DESCRIPTOR_CURRENT_VERSION_TOTAL_SIZE, + EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION_TOTAL_SIZE, + EBPF_PROGRAM_INFORMATION_CURRENT_VERSION_TOTAL_SIZE, + EBPF_HELPER_FUNCTION_ADDRESSES_CURRENT_VERSION_TOTAL_SIZE, + EBPF_PROGRAM_DATA_CURRENT_VERSION_TOTAL_SIZE, + EBPF_PROGRAM_SECTION_INFORMATION_CURRENT_VERSION_TOTAL_SIZE, + EBPF_MAP_PROVIDER_DATA_CURRENT_VERSION_TOTAL_SIZE, + EBPF_MAP_CLIENT_DATA_CURRENT_VERSION_TOTAL_SIZE, + EBPF_BASE_MAP_PROVIDER_DISPATCH_TABLE_CURRENT_VERSION_TOTAL_SIZE, + EBPF_BASE_MAP_PROVIDER_PROPERTIES_CURRENT_VERSION_TOTAL_SIZE, + EBPF_BASE_MAP_CLIENT_DISPATCH_TABLE_CURRENT_VERSION_TOTAL_SIZE, + EBPF_BTF_RESOLVED_FUNCTION_PROTOTYPE_CURRENT_VERSION_TOTAL_SIZE, + EBPF_BTF_RESOLVED_FUNCTION_PROVIDER_DATA_CURRENT_VERSION_TOTAL_SIZE, + + // eBPF native module object maximum total sizes. + EBPF_NATIVE_HELPER_FUNCTION_ENTRY_CURRENT_VERSION_TOTAL_SIZE, + EBPF_NATIVE_HELPER_FUNCTION_DATA_CURRENT_VERSION_TOTAL_SIZE, + EBPF_NATIVE_MAP_ENTRY_CURRENT_VERSION_TOTAL_SIZE, + EBPF_NATIVE_MAP_DATA_CURRENT_VERSION_TOTAL_SIZE, + EBPF_NATIVE_PROGRAM_ENTRY_CURRENT_VERSION_TOTAL_SIZE, + EBPF_NATIVE_PROGRAM_RUNTIME_CONTEXT_CURRENT_VERSION_TOTAL_SIZE, + EBPF_NATIVE_MAP_INITIAL_VALUES_CURRENT_VERSION_TOTAL_SIZE, + EBPF_NATIVE_GLOBAL_VARIABLE_SECTION_INFO_CURRENT_VERSION_TOTAL_SIZE, + EBPF_NATIVE_GLOBAL_VARIABLE_SECTION_DATA_CURRENT_VERSION_TOTAL_SIZE, +}; + #define EBPF_ATTACH_PROVIDER_DATA_SIZE_1 EBPF_SIZE_INCLUDING_FIELD(ebpf_attach_provider_data_t, link_type) size_t _ebpf_attach_provider_data_supported_size[] = {EBPF_ATTACH_PROVIDER_DATA_SIZE_1}; @@ -215,9 +246,22 @@ _ebpf_validate_extension_object_header( uint16_t count = _ebpf_extension_type_supported_sizes[object_type].count; __analysis_assume(supported_sizes != NULL); - return ( - (header->version == _supported_ebpf_extension_version[object_type]) && - (_ebpf_is_size_supported(supported_sizes, count, header->size))); + if (header->version != _supported_ebpf_extension_version[object_type]) { + return false; + } + + if (!_ebpf_is_size_supported(supported_sizes, count, header->size)) { + return false; + } + + // "total_size" is used as an array stride and as a copy length, so it must be bounded above: it can never + // exceed the size of the current version of the structure being copied into. Values smaller than "size" + // (including 0, which existing producers may leave unset) are not memory-unsafe and are tolerated. + if (header->total_size > _ebpf_extension_type_max_total_size[object_type]) { + return false; + } + + return true; } #ifndef GUID_NULL @@ -302,6 +346,10 @@ ebpf_validate_helper_function_prototype_array( if (helper_prototype_array == NULL) { return false; } + // Validate the first element before using its "total_size" as the array stride. + if (!_ebpf_validate_helper_function_prototype(helper_prototype_array)) { + return false; + } // Use "total_size" to calculate the actual size of the ebpf_helper_function_prototype_t struct. size_t helper_prototype_size = helper_prototype_array[0].header.total_size; for (uint32_t i = 0; i < count; i++) { @@ -528,7 +576,7 @@ _duplicate_helper_function_prototype_array( for (uint32_t i = 0; i < count; i++) { ebpf_helper_function_prototype_t* helper_prototype = (ebpf_helper_function_prototype_t*)ARRAY_ELEMENT_INDEX(helper_prototype_array, i, helper_prototype_size); - memcpy(&local_helper_prototype_array[i], helper_prototype, helper_prototype_size); + memcpy(&local_helper_prototype_array[i], helper_prototype, min(helper_prototype_size, sizeof(*helper_prototype))); local_helper_prototype_array[i].header.version = EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION; local_helper_prototype_array[i].header.size = EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION_SIZE; local_helper_prototype_array[i].header.total_size = EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION_TOTAL_SIZE; @@ -538,8 +586,9 @@ _duplicate_helper_function_prototype_array( result = EBPF_NO_MEMORY; goto Exit; } - if (local_helper_prototype_array[i].header.size == EBPF_HELPER_FUNCTION_PROTOTYPE_CURRENT_VERSION) { - local_helper_prototype_array[i].flags = helper_prototype[i].flags; + // Only copy the "flags" field if the source prototype is large enough to contain it. + if (helper_prototype->header.size >= EBPF_HELPER_FUNCTION_PROTOTYPE_SIZE_1) { + local_helper_prototype_array[i].flags = helper_prototype->flags; } }