Fix load errors for empty (but existent) BTF/BTF.ext sections (#608)

* use the hdr_len of BTF.ext sections rather than size of struct

Otherwise this will erroneously fail on older btf_ext_header that have
less fields than the bindgen'd struct

* do not attempt to load a BTF object that has no types

* add tests

* fix: hdr_len i32 -> u32

* guard against a bigger header in the future

* use separate unsafe blocks

* simplify writing to zero'd out header

* merge safe block and address typo
pull/613/head
Andrés 1 year ago committed by GitHub
parent 58f1ecbf00
commit 5894c4ce82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -210,6 +210,11 @@ impl Btf {
}
}
pub(crate) fn is_empty(&self) -> bool {
// the first one is awlays BtfType::Unknown
self.types.types.len() < 2
}
pub(crate) fn types(&self) -> impl Iterator<Item = &BtfType> {
self.types.types.iter()
}
@ -628,7 +633,10 @@ impl Object {
&mut self,
features: &BtfFeatures,
) -> Result<Option<&Btf>, BtfError> {
if let Some(ref mut obj_btf) = self.btf {
if let Some(ref mut obj_btf) = &mut self.btf {
if obj_btf.is_empty() {
return Ok(None);
}
// fixup btf
obj_btf.fixup_and_sanitize(
&self.section_sizes,
@ -667,13 +675,65 @@ impl BtfExt {
endianness: Endianness,
btf: &Btf,
) -> Result<BtfExt, BtfError> {
// Safety: btf_ext_header is POD so read_unaligned is safe
let header = unsafe {
ptr::read_unaligned::<btf_ext_header>(data.as_ptr() as *const btf_ext_header)
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct MinimalHeader {
pub magic: u16,
pub version: u8,
pub flags: u8,
pub hdr_len: u32,
}
if data.len() < std::mem::size_of::<MinimalHeader>() {
return Err(BtfError::InvalidHeader);
}
let header = {
// first find the actual size of the header by converting into the minimal valid header
// Safety: MinimalHeader is POD so read_unaligned is safe
let minimal_header = unsafe {
ptr::read_unaligned::<MinimalHeader>(data.as_ptr() as *const MinimalHeader)
};
let len_to_read = minimal_header.hdr_len as usize;
// prevent invalid input from causing UB
if data.len() < len_to_read {
return Err(BtfError::InvalidHeader);
}
// forwards compatibility: if newer headers are bigger
// than the pre-generated btf_ext_header we should only
// read up to btf_ext_header
let len_to_read = len_to_read.min(std::mem::size_of::<btf_ext_header>());
// now create our full-fledge header; but start with it
// zeroed out so unavailable fields stay as zero on older
// BTF.ext sections
let mut header = std::mem::MaybeUninit::<btf_ext_header>::zeroed();
// Safety: we have checked that len_to_read is less than
// size_of::<btf_ext_header> and less than
// data.len(). Additionally, we know that the header has
// been initialized so it's safe to call for assume_init.
unsafe {
std::ptr::copy(data.as_ptr(), header.as_mut_ptr() as *mut u8, len_to_read);
header.assume_init()
}
};
let btf_ext_header {
hdr_len,
func_info_off,
func_info_len,
line_info_off,
line_info_len,
core_relo_off,
core_relo_len,
..
} = header;
let rec_size = |offset, len| {
let offset = mem::size_of::<btf_ext_header>() + offset as usize;
let offset = hdr_len as usize + offset as usize;
let len = len as usize;
// check that there's at least enough space for the `rec_size` field
if (len > 0 && len < 4) || offset + len > data.len() {
@ -695,16 +755,6 @@ impl BtfExt {
})
};
let btf_ext_header {
func_info_off,
func_info_len,
line_info_off,
line_info_len,
core_relo_off,
core_relo_len,
..
} = header;
let mut ext = BtfExt {
header,
relocations: Vec::new(),
@ -1039,6 +1089,21 @@ mod tests {
}
}
#[test]
fn parsing_older_ext_data() {
let btf_data = [
159, 235, 1, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
];
let btf_ext_data = [
159, 235, 1, 0, 24, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0,
0, 16, 0, 0, 0,
];
let btf = Btf::parse(&btf_data, Endianness::default()).unwrap();
let btf_ext = BtfExt::parse(&btf_ext_data, Endianness::default(), &btf).unwrap();
assert_eq!(btf_ext.func_info_rec_size(), 8);
assert_eq!(btf_ext.line_info_rec_size(), 16);
}
#[test]
fn test_write_btf() {
let mut btf = Btf::new();

@ -1634,6 +1634,31 @@ mod tests {
)
}
#[test]
fn sanitizes_empty_btf_files_to_none() {
let mut obj = fake_obj();
obj.parse_section(fake_section(
BpfSectionKind::Btf,
".BTF",
&[
159, 235, 1, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
],
))
.unwrap();
obj.parse_section(fake_section(
BpfSectionKind::BtfExt,
".BTF.ext",
&[
159, 235, 1, 0, 24, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 8, 0,
0, 0, 16, 0, 0, 0,
],
))
.unwrap();
let btf = obj.fixup_and_sanitize_btf(&BtfFeatures::default()).unwrap();
assert!(btf.is_none());
}
#[test]
fn test_parse_program_error() {
let obj = fake_obj();

Loading…
Cancel
Save