Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions aya-obj/src/btf/btf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,27 @@ pub enum BtfError {
/// unable to get symbol name
#[error("Unable to get symbol name")]
InvalidSymbolName,

/// Inner map definition cannot be pinned.
#[error("BTF map `{name}`: inner map definition cannot be pinned")]
InnerMapCannotBePinned {
/// The name of the map with the invalid definition.
name: String,
},

/// Multi-level map-in-map is not supported.
#[error("BTF map `{name}`: multi-level map-in-map is not supported")]
MultiLevelMapInMapNotSupported {
/// The name of the map with the invalid definition.
name: String,
},

/// The `values` spec must be a zero-sized array.
#[error("BTF map `{name}`: `values` spec is not a zero-sized array")]
InvalidValuesSpec {
/// The name of the map with the invalid definition.
name: String,
},
}

/// Available BTF features
Expand Down
58 changes: 58 additions & 0 deletions aya-obj/src/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,60 @@ impl Map {
Self::Btf(m) => Some(m.symbol_index),
}
}

/// Returns the inner map definition, in case of a map of maps.
///
/// The inner map is a synthetic object with no ELF presence of its own,
/// so neutral metadata values are used.
pub fn inner(&self) -> Option<Self> {
match self {
Self::Legacy(m) => m.inner_def.as_ref().map(|inner_def| {
Self::Legacy(LegacyMap {
def: *inner_def,
inner_def: None,
section_index: 0,
section_kind: EbpfSectionKind::Undefined,
symbol_index: None,
data: Vec::new(),
})
}),
Self::Btf(m) => m.inner_def.as_ref().map(|inner_def| {
Self::Btf(BtfMap {
def: *inner_def,
inner_def: None,
section_index: 0,
symbol_index: 0,
data: Vec::new(),
})
}),
}
}

/// Creates a new map definition from raw parameters.
pub const fn new_from_params(
map_type: u32,
key_size: u32,
value_size: u32,
max_entries: u32,
flags: u32,
) -> Self {
Self::Legacy(LegacyMap {
def: bpf_map_def {
map_type,
key_size,
value_size,
max_entries,
map_flags: flags,
id: 0,
pinning: PinningType::None,
},
inner_def: None,
section_index: 0,
section_kind: EbpfSectionKind::Undefined,
symbol_index: None,
data: Vec::new(),
})
}
}

/// A map declared with legacy BPF map declaration style, most likely from a `maps` section.
Expand All @@ -268,6 +322,8 @@ impl Map {
pub struct LegacyMap {
/// The definition of the map
pub def: bpf_map_def,
/// The definition of the inner map, in case of a map of maps.
pub inner_def: Option<bpf_map_def>,
/// The section index
pub section_index: usize,
/// The section kind
Expand All @@ -287,6 +343,8 @@ pub struct LegacyMap {
pub struct BtfMap {
/// The definition of the map
pub def: BtfMapDef,
/// The definition of the inner map, in case of a map of maps.
pub(crate) inner_def: Option<BtfMapDef>,
pub(crate) section_index: usize,
pub(crate) symbol_index: usize,
pub(crate) data: Vec<u8>,
Expand Down
84 changes: 80 additions & 4 deletions aya-obj/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use object::{
use crate::{
btf::{
Array, Btf, BtfError, BtfExt, BtfFeatures, BtfType, DataSecEntry, FuncSecInfo, LineSecInfo,
Struct,
},
generated::{
BPF_CALL, BPF_F_RDONLY_PROG, BPF_JMP, BPF_K, bpf_func_id, bpf_insn, bpf_map_info,
Expand Down Expand Up @@ -785,7 +786,7 @@ impl Object {
if type_name == section.name {
// each btf_var_secinfo contains a map
for info in &datasec.entries {
let (map_name, def) = parse_btf_map_def(btf, info)?;
let (map_name, def, inner_def) = parse_btf_map_def(btf, info)?;
let symbol_index =
maps.get(&map_name)
.ok_or_else(|| ParseError::SymbolNotFound {
Expand All @@ -795,6 +796,7 @@ impl Object {
map_name,
Map::Btf(BtfMap {
def,
inner_def,
section_index: section.index.0,
symbol_index: *symbol_index,
data: Vec::new(),
Expand Down Expand Up @@ -840,6 +842,7 @@ impl Object {
section_kind: section.kind,
symbol_index: Some(sym.index),
def,
inner_def: None,
data: Vec::new(),
}),
);
Expand Down Expand Up @@ -1255,6 +1258,7 @@ fn parse_data_map_section(section: &Section<'_>) -> Map {
// Data maps don't require symbols to be relocated
symbol_index: None,
def,
inner_def: None,
data,
})
}
Expand All @@ -1278,7 +1282,10 @@ fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
}
}

fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDef), BtfError> {
fn parse_btf_map_def(
btf: &Btf,
info: &DataSecEntry,
) -> Result<(String, BtfMapDef, Option<BtfMapDef>), BtfError> {
let ty = match btf.type_by_id(info.btf_type)? {
BtfType::Var(var) => var,
other => {
Expand All @@ -1288,7 +1295,6 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe
}
};
let map_name = btf.string_at(ty.name_offset)?;
let mut map_def = BtfMapDef::default();

let root_type = btf.resolve_type(ty.btf_type)?;
let s = match btf.type_by_id(root_type)? {
Expand All @@ -1300,6 +1306,23 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe
}
};

let (map_def, inner_def) = parse_btf_map_struct(btf, s, &map_name, false)?;
Ok((map_name.to_string(), map_def, inner_def))
}

/// Parses BTF struct members into a map definition.
///
/// When `is_inner` is true, rejects `values` and `pinning` fields, matching
/// libbpf behavior for inner map definitions.
fn parse_btf_map_struct(
btf: &Btf,
s: &Struct,
map_name: &str,
is_inner: bool,
) -> Result<(BtfMapDef, Option<BtfMapDef>), BtfError> {
let mut map_def = BtfMapDef::default();
let mut inner_map_def = None;

for m in &s.members {
match btf.string_at(m.name_offset)?.as_ref() {
"type" => {
Expand Down Expand Up @@ -1343,18 +1366,67 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe
map_def.map_extra = get_map_field(btf, m.btf_type)?.into();
}
"pinning" => {
if is_inner {
return Err(BtfError::InnerMapCannotBePinned {
name: map_name.to_owned(),
});
}
let pinning = get_map_field(btf, m.btf_type)?;
map_def.pinning = PinningType::try_from(pinning).unwrap_or_else(|_| {
debug!("{pinning} is not a valid pin type. using PIN_NONE");
PinningType::None
});
}
"values" => {
if is_inner {
return Err(BtfError::MultiLevelMapInMapNotSupported {
name: map_name.to_owned(),
});
}
// The inner map type is encoded as a zero-length array of pointers
// to the inner struct, matching libbpf's parse_btf_map_def().
let arr = match btf.type_by_id(m.btf_type)? {
BtfType::Array(Array { array, .. }) => array,
other => {
return Err(BtfError::UnexpectedBtfType {
type_id: other.btf_type().unwrap_or(0),
});
}
};
if arr.len != 0 {
return Err(BtfError::InvalidValuesSpec {
Comment on lines +1396 to +1397
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept initialized values arrays

This rejects valid libbpf-style map-in-map definitions that prepopulate the outer map with inner maps, where __array(values, struct inner_map) is initialized with slots such as .values = { [0] = &inner } and the BTF array length is therefore non-zero. In that scenario Aya returns InvalidValuesSpec while parsing the object, so programs that previously load with libbpf cannot be loaded even though the values field is the standard way to encode map-in-map templates and initial contents.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

False positive. __array expands to typeof(val) *name[], so clang always emits BTF nelems=0 regardless of .values initializers. libbpf's parse_btf_map_def does the same check.

name: map_name.to_owned(),
});
}
let elem_type_id = btf.resolve_type(arr.element_type)?;
let ptr = match btf.type_by_id(elem_type_id)? {
BtfType::Ptr(ptr) => ptr,
other => {
return Err(BtfError::UnexpectedBtfType {
type_id: other.btf_type().unwrap_or(0),
});
}
};
let inner_struct_id = btf.resolve_type(ptr.btf_type)?;
let inner_s = match btf.type_by_id(inner_struct_id)? {
BtfType::Struct(s) => s,
other => {
return Err(BtfError::UnexpectedBtfType {
type_id: other.btf_type().unwrap_or(0),
});
}
};
let (inner_def, _) = parse_btf_map_struct(btf, inner_s, map_name, true)?;
inner_map_def = Some(inner_def);
// Map-of-maps value is always an fd (u32).
map_def.value_size = size_of::<u32>() as u32;
}
other => {
debug!("skipping unknown map section: {other}");
}
}
}
Ok((map_name.to_string(), map_def))
Ok((map_def, inner_map_def))
}

/// Parses a [`bpf_map_info`] into a [`Map`].
Expand All @@ -1375,6 +1447,7 @@ pub const fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map {
btf_key_type_id: info.btf_key_type_id,
btf_value_type_id: info.btf_value_type_id,
},
inner_def: None,
section_index: 0,
symbol_index: 0,
data: Vec::new(),
Expand All @@ -1390,6 +1463,7 @@ pub const fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map {
pinning: pinned,
id: info.id,
},
inner_def: None,
section_index: 0,
symbol_index: None,
section_kind: EbpfSectionKind::Undefined,
Expand Down Expand Up @@ -1673,6 +1747,7 @@ mod tests {
pinning: PinningType::None,
},
data,
..
}) if data == map_data && value_size == map_data.len() as u32
)
}
Expand Down Expand Up @@ -2728,6 +2803,7 @@ mod tests {
id: 1,
pinning: PinningType::None,
},
inner_def: None,
section_index: 1,
section_kind: EbpfSectionKind::Rodata,
symbol_index: Some(1),
Expand Down
2 changes: 2 additions & 0 deletions aya-obj/src/relocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ mod test {
fn fake_legacy_map(symbol_index: usize) -> Map {
Map::Legacy(LegacyMap {
def: Default::default(),
inner_def: None,
section_index: 0,
section_kind: EbpfSectionKind::Undefined,
symbol_index: Some(symbol_index),
Expand All @@ -526,6 +527,7 @@ mod test {
fn fake_btf_map(symbol_index: usize) -> Map {
Map::Btf(BtfMap {
def: Default::default(),
inner_def: None,
section_index: 0,
symbol_index,
data: Vec::new(),
Expand Down
Loading