From 5894c4ce82948c7e5fe766f41b690d036fcca907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= <84341363+rc-andres@users.noreply.github.com> Date: Thu, 25 May 2023 01:04:03 -0700 Subject: [PATCH] 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 --- aya-obj/src/btf/btf.rs | 95 +++++++++++++++++++++++++++++++++++------- aya-obj/src/obj.rs | 25 +++++++++++ 2 files changed, 105 insertions(+), 15 deletions(-) diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index 325a8aef..e94b67be 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -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 { self.types.types.iter() } @@ -628,7 +633,10 @@ impl Object { &mut self, features: &BtfFeatures, ) -> Result, 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 { - // Safety: btf_ext_header is POD so read_unaligned is safe - let header = unsafe { - ptr::read_unaligned::(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::() { + 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::(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::()); + + // 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::::zeroed(); + // Safety: we have checked that len_to_read is less than + // size_of:: 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::() + 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(); diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index 773d2930..7536dda8 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -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();