Skip to content

Commit

Permalink
Make bpf2c maps parser match verifier's maps parser
Browse files Browse the repository at this point in the history
The verifier will parse "maps/*" sections, and is able to deal with
maps sections with various record sizes.  This PR updates bpf2c to
use the same algorithm.  In the future it would be good to refactor
the verifier so the same code can be used.

Fixes #900

Signed-off-by: Dave Thaler <dthaler@microsoft.com>
  • Loading branch information
dthaler committed Jun 3, 2023
1 parent f02a98e commit 4fda276
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 47 deletions.
4 changes: 2 additions & 2 deletions tests/sample/map_in_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include "bpf_helpers.h"

SEC("maps")
SEC("maps/test")
struct bpf_map_def outer_map = {
.type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
.key_size = sizeof(uint32_t),
Expand All @@ -22,7 +22,7 @@ struct bpf_map_def outer_map = {
// inner_map_idx refers to the map index in the same ELF object.
.inner_map_idx = 1}; // (uint32_t)&inner_map

SEC("maps")
SEC("maps/test")
struct bpf_map_def inner_map = {
.type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint32_t), .max_entries = 1};

Expand Down
144 changes: 99 additions & 45 deletions tools/bpf2c/bpf_code_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,62 +354,116 @@ bpf_code_generator::extract_program(const bpf_code_generator::unsafe_string& sec
}
}

// Legacy (non-BTF) maps sections are identified as any section called "maps", or matching "maps/<map-name>".
static bool
is_legacy_map_section(const std::string& name)
{
std::string maps_prefix = "maps/";
return name == "maps" || (name.length() > 5 && name.compare(0, maps_prefix.length(), maps_prefix) == 0);
}

void
bpf_code_generator::parse()
{
auto map_section = get_optional_section("maps");
if (map_section) {
ELFIO::const_symbol_section_accessor symbols{reader, get_required_section(".symtab")};
size_t data_size = map_section->get_size();
size_t map_count = data_size / sizeof(ebpf_map_definition_in_file_t);

if (data_size % sizeof(ebpf_map_definition_in_file_t) != 0) {
throw bpf_code_generator_exception(
"bad maps section size, must be a multiple of " +
std::to_string(sizeof(ebpf_map_definition_in_file_t)));
// Parse all maps sections.
for (auto& section : reader.sections) {
std::string name = section->get_name();
if (is_legacy_map_section(name)) {
parse_legacy_maps_section(name);
}
}
}

for (ELFIO::Elf_Xword i = 0; i < symbols.get_symbols_num(); i++) {
std::string unsafe_symbol_name;
ELFIO::Elf64_Addr symbol_value{};
unsigned char symbol_bind{};
unsigned char symbol_type{};
ELFIO::Elf_Half symbol_section_index{};
unsigned char symbol_other{};
ELFIO::Elf_Xword symbol_size{};

symbols.get_symbol(
i,
unsafe_symbol_name,
symbol_value,
symbol_size,
symbol_bind,
symbol_type,
symbol_section_index,
symbol_other);

if (symbol_section_index == map_section->get_index()) {
if (symbol_size != sizeof(ebpf_map_definition_in_file_t)) {
throw bpf_code_generator_exception("invalid map size");
}
if (symbol_value > map_section->get_size()) {
throw bpf_code_generator_exception("invalid symbol value");
}
if ((symbol_value + symbol_size) > map_section->get_size()) {
throw bpf_code_generator_exception("invalid symbol value");
}
static std::tuple<std::string, ELFIO::Elf_Half>
get_symbol_name_and_section_index(ELFIO::const_symbol_section_accessor& symbols, ELFIO::Elf_Xword index)
{
std::string symbol_name;
ELFIO::Elf64_Addr value{};
ELFIO::Elf_Xword size{};
unsigned char bind{};
unsigned char type{};
ELFIO::Elf_Half section_index{};
unsigned char other{};
symbols.get_symbol(index, symbol_name, value, size, bind, type, section_index, other);
return {symbol_name, section_index};
}

map_definitions[unsafe_symbol_name].definition =
*reinterpret_cast<const ebpf_map_definition_in_file_t*>(map_section->get_data() + symbol_value);
// We should consider refactoring the code that parses ELF files into a form that can be used by both ebpf-verifier and
// bpf2c.
void
bpf_code_generator::parse_legacy_maps_section(const unsafe_string& name)
{
auto map_section = get_optional_section(name);
if (!map_section) {
return;
}

map_definitions[unsafe_symbol_name].index = symbol_value / sizeof(ebpf_map_definition_in_file_t);
// Count the number of symbols that point into this maps section.
ELFIO::const_symbol_section_accessor symbols{reader, get_required_section(".symtab")};
int map_count = 0;
for (ELFIO::Elf_Xword index = 0; index < symbols.get_symbols_num(); index++) {
auto [symbol_name, section_index] = get_symbol_name_and_section_index(symbols, index);
if ((section_index == map_section->get_index()) && !symbol_name.empty())
map_count++;
}
if (map_count == 0) {
return;
}

size_t data_size = map_section->get_size();
size_t map_record_size = data_size / map_count;

if (data_size % map_record_size != 0) {
throw bpf_code_generator_exception(
"bad maps section size, must be a multiple of " + std::to_string(map_record_size));
}

size_t old_map_count = map_definitions.size();
for (ELFIO::Elf_Xword i = 0; i < symbols.get_symbols_num(); i++) {
std::string unsafe_symbol_name;
ELFIO::Elf64_Addr symbol_value{};
unsigned char symbol_bind{};
unsigned char symbol_type{};
ELFIO::Elf_Half symbol_section_index{};
unsigned char symbol_other{};
ELFIO::Elf_Xword symbol_size{};

symbols.get_symbol(
i,
unsafe_symbol_name,
symbol_value,
symbol_size,
symbol_bind,
symbol_type,
symbol_section_index,
symbol_other);

if (symbol_section_index == map_section->get_index()) {
if (symbol_size != map_record_size) {
throw bpf_code_generator_exception("invalid map size");
}
if (symbol_value > map_section->get_size()) {
throw bpf_code_generator_exception("invalid symbol value");
}
if ((symbol_value + symbol_size) > map_section->get_size()) {
throw bpf_code_generator_exception("invalid symbol value");
}
}

if (map_definitions.size() != map_count) {
throw bpf_code_generator_exception("bad maps section, map must have associated symbol");
// Copy the data from the record into an ebpf_map_definition_in_file_t structure,
// zero-padding any extra, and being careful not to overflow the buffer.
map_definitions[unsafe_symbol_name].definition = {};
memcpy(
&map_definitions[unsafe_symbol_name].definition,
map_section->get_data() + symbol_value,
min(sizeof(map_definitions[unsafe_symbol_name].definition), map_record_size));

map_definitions[unsafe_symbol_name].index = symbol_value / map_record_size;
}
}

if (map_definitions.size() != old_map_count + map_count) {
throw bpf_code_generator_exception("bad maps section, map must have associated symbol");
}
}

void
Expand Down
8 changes: 8 additions & 0 deletions tools/bpf2c/bpf_code_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ class bpf_code_generator
void
parse();

/**
* @brief Parse legacy map information in the eBPF file.
*
* @param[in] section_name Section in the ELF file to parse legacy maps in.
*/
void
parse_legacy_maps_section(const unsafe_string& name);

/**
* @brief Generate C code from the parsed eBPF file.
*
Expand Down

0 comments on commit 4fda276

Please sign in to comment.