From b99ce5f9f42c07363c1b60477c3ef5c0c629b702 Mon Sep 17 00:00:00 2001 From: rbran Date: Thu, 9 Jan 2025 13:09:55 -0300 Subject: [PATCH] add a permissive feature The permissive feature will make allow a more "relaxed" parsing of files, making fewer checks and defaulting to 0 if EoF is found. --- Cargo.toml | 4 ++ src/ida_reader.rs | 130 ++++++++++++++++++++++++++++++++++++++------ src/lib.rs | 68 +++++++++++++---------- src/til.rs | 10 +++- src/til/array.rs | 17 +++--- src/til/bitfield.rs | 22 +++++++- src/til/enum.rs | 62 +++++++++++---------- src/til/function.rs | 24 +++++--- src/til/pointer.rs | 12 ++-- src/til/section.rs | 10 +++- src/til/struct.rs | 28 +++++++--- src/til/union.rs | 28 ++++++---- 12 files changed, 297 insertions(+), 118 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1e45a7c..3d2d581 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,10 @@ flate2 = "1.0.31" serde = { version = "1.0", features = ["derive"] } num_enum = "0.7.3" +[features] +default = [] +permissive = [] + [[bin]] name = "idb-tools" path = "src/tools/tools.rs" diff --git a/src/ida_reader.rs b/src/ida_reader.rs index 57fc053..f314bed 100644 --- a/src/ida_reader.rs +++ b/src/ida_reader.rs @@ -43,9 +43,12 @@ pub trait IdaUnpack: IdaGenericUnpack { if self.is_64() { let start = self.unpack_dq()?; let len = self.unpack_dq()?; + #[cfg(not(feature = "permissive"))] let end = start .checked_add(len) .ok_or_else(|| anyhow!("Function range overflows"))?; + #[cfg(feature = "permissive")] + let end = start.saturating_add(len); Ok(start..end) } else { let start = self.unpack_dd_ext_max()?; @@ -54,7 +57,10 @@ pub trait IdaUnpack: IdaGenericUnpack { let end = match start.checked_add(len.into()) { Some(0xFFFF_FFFF) => u64::MAX, Some(value) => value, + #[cfg(not(feature = "permissive"))] None => return Err(anyhow!("Function range overflows")), + #[cfg(feature = "permissive")] + None => u64::MAX, }; Ok(start..end) } @@ -172,11 +178,14 @@ pub trait IdaGenericBufUnpack: IdaGenericUnpack + BufRead { let mut nelem = 0; // TODO check no more then 9 bytes are read loop { - let Some(typ) = self.fill_buf()?.first().copied() else { + let Some(typ) = self.peek_u8()? else { + #[cfg(not(feature = "permissive"))] return Err(anyhow!(std::io::Error::new( std::io::ErrorKind::UnexpectedEof, "Unexpected EoF on DA" ))); + #[cfg(feature = "permissive")] + return Ok((nelem, base)); }; if typ & 0x80 == 0 { break; @@ -192,11 +201,14 @@ pub trait IdaGenericBufUnpack: IdaGenericUnpack + BufRead { } nelem = (z >> 4) & 7; loop { - let Some(y) = self.fill_buf()?.first().copied() else { + let Some(y) = self.peek_u8()? else { + #[cfg(not(feature = "permissive"))] return Err(anyhow!(std::io::Error::new( std::io::ErrorKind::UnexpectedEof, "Unexpected EoF on DA" ))); + #[cfg(feature = "permissive")] + return Ok((nelem, base)); }; if (y & 0x80) == 0 { break; @@ -218,7 +230,13 @@ pub trait IdaGenericBufUnpack: IdaGenericUnpack + BufRead { let mut buf = vec![]; self.read_until(b'\x00', &mut buf)?; // last char need to be \x00 or we found a EoF - ensure!(buf.pop() == Some(b'\x00'), "Unexpected EoF on CStr"); + if let Some(b'\x00') = buf.last() { + let _ = buf.pop(); // remove the \x00 from the end + } else { + // found EOF, aka could not find the \x00 for the string end + #[cfg(not(feature = "permissive"))] + return Err(anyhow!("Unexpected EoF on CStr")); + } Ok(buf) } @@ -249,6 +267,7 @@ pub trait IdaGenericBufUnpack: IdaGenericUnpack + BufRead { if (start_value >> bit) & 1 != 0 { let value = self.read_u8()?; // TODO is this an error or expect possible value? + #[cfg(not(feature = "permissive"))] ensure!(value != 0); acc |= (value as u64) << byte; } @@ -301,6 +320,13 @@ pub trait IdaGenericUnpack: Read { Ok(data[0]) } + #[cfg(feature = "permissive")] + fn read_u8_or_nothing(&mut self) -> Result> { + let mut data = [0; 1]; + let read = self.read_exact_or_nothing(&mut data)?; + Ok((read == data.len()).then_some(data[0])) + } + fn read_u16(&mut self) -> Result { let mut data = [0; 2]; self.read_exact(&mut data)?; @@ -339,7 +365,13 @@ pub trait IdaGenericUnpack: Read { // NOTE: the original implementation never fails, if input hit EoF it a partial result or 0 /// Reads 1 to 3 bytes. fn unpack_dw(&mut self) -> Result { - let b1: u8 = bincode::deserialize_from(&mut *self)?; + #[cfg(not(feature = "permissive"))] + let b1 = self.read_u8()?; + #[cfg(feature = "permissive")] + let Some(b1) = self.read_u8_or_nothing()? + else { + return Ok(0); + }; match b1 { // 7 bit value // [0xxx xxxx] @@ -347,7 +379,10 @@ pub trait IdaGenericUnpack: Read { // 14 bits value // [10xx xxxx] xxxx xxxx 0x80..=0xBF => { - let lo: u8 = bincode::deserialize_from(&mut *self)?; + #[cfg(not(feature = "permissive"))] + let lo = self.read_u8()?; + #[cfg(feature = "permissive")] + let lo = self.read_u8_or_nothing()?.unwrap_or(0); Ok(u16::from_be_bytes([b1 & 0x3F, lo])) } // 16 bits value @@ -355,7 +390,16 @@ pub trait IdaGenericUnpack: Read { 0xC0..=0xFF => { // NOTE first byte 6 bits seems to be ignored //ensure!(header != 0xC0 && header != 0xFF); - Ok(u16::from_be_bytes(bincode::deserialize_from(&mut *self)?)) + #[cfg(not(feature = "permissive"))] + let (lo, hi) = (self.read_u8()?, self.read_u8()?); + + #[cfg(feature = "permissive")] + let (lo, hi) = ( + self.read_u8_or_nothing()?.unwrap_or(0), + self.read_u8_or_nothing()?.unwrap_or(0), + ); + + Ok(u16::from_be_bytes([lo, hi])) } } } @@ -364,7 +408,13 @@ pub trait IdaGenericUnpack: Read { // NOTE the orignal implementation never fails, if input hit EoF it a partial result or 0 /// Reads 1 to 5 bytes. fn unpack_dd(&mut self) -> Result { - let b1: u8 = bincode::deserialize_from(&mut *self)?; + #[cfg(not(feature = "permissive"))] + let b1 = self.read_u8()?; + #[cfg(feature = "permissive")] + let Some(b1) = self.read_u8_or_nothing()? + else { + return Ok(0); + }; match b1 { // 7 bit value // [0xxx xxxx] @@ -372,13 +422,20 @@ pub trait IdaGenericUnpack: Read { // 14 bits value // [10xx xxxx] xxxx xxxx 0x80..=0xBF => { - let lo: u8 = bincode::deserialize_from(&mut *self)?; + #[cfg(not(feature = "permissive"))] + let lo = self.read_u8()?; + #[cfg(feature = "permissive")] + let lo = self.read_u8_or_nothing()?.unwrap_or(0); Ok(u32::from_be_bytes([0, 0, b1 & 0x3F, lo])) } // 29 bit value: // [110x xxxx] xxxx xxxx xxxx xxxx xxxx xxxx 0xC0..=0xDF => { - let bytes: [u8; 3] = bincode::deserialize_from(&mut *self)?; + let mut bytes: [u8; 3] = [0; 3]; + #[cfg(not(feature = "permissive"))] + self.read_exact(&mut bytes)?; + #[cfg(feature = "permissive")] + let _size = self.read_exact_or_nothing(&mut bytes)?; Ok(u32::from_be_bytes([ b1 & 0x1F, bytes[0], @@ -391,7 +448,12 @@ pub trait IdaGenericUnpack: Read { 0xE0..=0xFF => { // NOTE first byte 5 bits seems to be ignored //ensure!(header != 0xE0 && header != 0xFF); - Ok(u32::from_be_bytes(bincode::deserialize_from(&mut *self)?)) + let mut bytes: [u8; 4] = [0; 4]; + #[cfg(not(feature = "permissive"))] + self.read_exact(&mut bytes)?; + #[cfg(feature = "permissive")] + let _size = self.read_exact_or_nothing(&mut bytes)?; + Ok(u32::from_be_bytes(bytes)) } } } @@ -418,14 +480,20 @@ pub trait IdaGenericUnpack: Read { fn unpack_ds(&mut self) -> Result> { let len = self.unpack_dd()?; let mut result = vec![0; len.try_into()?]; + #[cfg(not(feature = "permissive"))] self.read_exact(&mut result)?; + #[cfg(feature = "permissive")] + let _size = self.read_exact_or_nothing(&mut result)?; Ok(result) } fn unpack_dt_bytes(&mut self) -> Result> { let buf_len = self.read_dt()?; let mut buf = vec![0; buf_len.into()]; + #[cfg(not(feature = "permissive"))] self.read_exact(&mut buf)?; + #[cfg(feature = "permissive")] + let _size = self.read_exact_or_nothing(&mut buf)?; Ok(buf) } @@ -437,13 +505,19 @@ pub trait IdaGenericUnpack: Read { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x48cdb0 let mut acc: u32 = 0; for _ in 0..5 { - let b: u32 = self.read_u8()?.into(); + #[cfg(not(feature = "permissive"))] + let b = self.read_u8()?; + #[cfg(feature = "permissive")] + let Some(b) = self.read_u8_or_nothing()? + else { + return Ok(acc); + }; if b & 0x80 == 0 { - acc = (b & 0x3F) | (acc << 6); + acc = (b & 0x3F) as u32 | (acc << 6); return Ok(acc); } - acc = (acc << 7) | (b & 0x7F); + acc = (acc << 7) | (b & 0x7F) as u32; } Err(anyhow!("Can't find the end of DE")) } @@ -454,12 +528,19 @@ pub trait IdaGenericUnpack: Read { fn read_dt(&mut self) -> Result { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x48cd60 let value = match self.read_u8()? { + #[cfg(not(feature = "permissive"))] 0 => return Err(anyhow!("DT can't have 0 value")), + #[cfg(feature = "permissive")] + 0 => return Ok(0), //SEG = 2 value @ 0x80.. => { - let inter: u16 = self.read_u8()?.into(); + #[cfg(not(feature = "permissive"))] + let inter = self.read_u8()?; + #[cfg(feature = "permissive")] + let inter = self.read_u8_or_nothing()?.unwrap_or(0); + #[cfg(not(feature = "permissive"))] ensure!(inter != 0, "DT can't have a following 0 value"); - value as u16 & 0x7F | inter << 7 + value as u16 & 0x7F | (inter as u16) << 7 } //SEG = 1 value @ ..=0x7F => value.into(), @@ -498,7 +579,16 @@ pub trait IdaGenericUnpack: Read { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x452830 fn read_type_attribute(&mut self) -> Result { use crate::til::flag::tattr_ext::*; - let byte0: u8 = self.read_u8()?; + #[cfg(not(feature = "permissive"))] + let byte0 = self.read_u8()?; + #[cfg(feature = "permissive")] + let Some(byte0) = self.read_u8_or_nothing()? + else { + return Ok(TypeAttribute { + tattr: 0, + extended: None, + }); + }; let mut tattr = 0; if byte0 != 0xfe { tattr = ((byte0 as u16 & 1) | ((byte0 as u16 >> 3) & 6)) + 1; @@ -508,7 +598,13 @@ pub trait IdaGenericUnpack: Read { let mut shift = 0; // TODO limit the loop to only 0..n loop { - let next_byte: u8 = self.read_u8()?; + #[cfg(not(feature = "permissive"))] + let next_byte = self.read_u8()?; + #[cfg(feature = "permissive")] + let Some(next_byte) = self.read_u8_or_nothing()? + else { + break; + }; ensure!( next_byte != 0, "Failed to parse TypeAttribute, byte is zero" diff --git a/src/lib.rs b/src/lib.rs index b5873b2..5c8f389 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#[forbid(unsafe_code)] pub mod id0; pub mod id1; pub(crate) mod ida_reader; @@ -111,7 +112,7 @@ impl IDBParser { let mut input = std::io::Read::take(&mut self.input, section_header.len); match section_header.compress { IDBSectionCompression::Zlib => { - let mut input = flate2::read::ZlibDecoder::new(input); + let mut input = flate2::bufread::ZlibDecoder::new(input); let _ = std::io::copy(&mut input, output)?; } IDBSectionCompression::None => { @@ -295,15 +296,18 @@ impl IDBHeader { ) -> Result { #[derive(Debug, Deserialize)] struct V1Raw { - id2_offset: u32, + _id2_offset: u32, checksums: [u32; 3], - unk30_zeroed: u32, + _unk30_zeroed: u32, unk33_checksum: u32, } let v1_raw: V1Raw = bincode::deserialize_from(input)?; - ensure!(v1_raw.unk30_zeroed == 0, "unk30 not zeroed"); - ensure!(v1_raw.id2_offset == 0, "id2 in V1 is not zeroed"); + #[cfg(not(feature = "permissive"))] + { + ensure!(v1_raw._unk30_zeroed == 0, "unk30 not zeroed"); + ensure!(v1_raw._id2_offset == 0, "id2 in V1 is not zeroed"); + } // TODO ensure all offsets point to after the header Ok(Self { @@ -328,20 +332,23 @@ impl IDBHeader { ) -> Result { #[derive(Debug, Deserialize)] struct V4Raw { - id2_offset: u32, + _id2_offset: u32, checksums: [u32; 3], - unk30_zeroed: u32, + _unk30_zeroed: u32, unk33_checksum: u32, - unk38_zeroed: [u8; 8], - unk40_v5c: u32, + _unk38_zeroed: [u8; 8], + _unk40_v5c: u32, } let v4_raw: V4Raw = bincode::deserialize_from(input)?; - ensure!(v4_raw.unk30_zeroed == 0, "unk30 not zeroed"); - ensure!(v4_raw.id2_offset == 0, "id2 in V4 is not zeroed"); - ensure!(v4_raw.unk38_zeroed == [0; 8], "unk38 is not zeroed"); - ensure!(v4_raw.unk40_v5c == 0x5c, "unk40 is not 0x5C"); + #[cfg(not(feature = "permissive"))] + { + ensure!(v4_raw._unk30_zeroed == 0, "unk30 not zeroed"); + ensure!(v4_raw._id2_offset == 0, "id2 in V4 is not zeroed"); + ensure!(v4_raw._unk38_zeroed == [0; 8], "unk38 is not zeroed"); + ensure!(v4_raw._unk40_v5c == 0x5c, "unk40 is not 0x5C"); + } // TODO ensure all offsets point to after the header Ok(Self { @@ -367,14 +374,14 @@ impl IDBHeader { #[derive(Debug, Deserialize)] struct V5Raw { nam_offset: u64, - seg_offset_zeroed: u64, + _seg_offset_zeroed: u64, til_offset: u64, initial_checksums: [u32; 3], - unk4_zeroed: u32, + _unk4_zeroed: u32, unk_checksum: u32, - id2_offset_zeroed: u64, + _id2_offset_zeroed: u64, final_checksum: u32, - unk0_v7c: u32, + _unk0_v7c: u32, } let v5_raw: V5Raw = bincode::deserialize_from(input)?; let id0_offset = @@ -383,11 +390,13 @@ impl IDBHeader { u64::from_le(u64::from(header_raw.offsets[3]) << 32 | u64::from(header_raw.offsets[2])); // TODO Final checksum is always zero on v5? - - ensure!(v5_raw.unk4_zeroed == 0, "unk4 not zeroed"); - ensure!(v5_raw.id2_offset_zeroed == 0, "id2 in V5 is not zeroed"); - ensure!(v5_raw.seg_offset_zeroed == 0, "seg in V5 is not zeroed"); - ensure!(v5_raw.unk0_v7c == 0x7C, "unk0 not 0x7C"); + #[cfg(not(feature = "permissive"))] + { + ensure!(v5_raw._unk4_zeroed == 0, "unk4 not zeroed"); + ensure!(v5_raw._id2_offset_zeroed == 0, "id2 in V5 is not zeroed"); + ensure!(v5_raw._seg_offset_zeroed == 0, "seg in V5 is not zeroed"); + ensure!(v5_raw._unk0_v7c == 0x7C, "unk0 not 0x7C"); + } // TODO ensure all offsets point to after the header Ok(Self { @@ -414,14 +423,14 @@ impl IDBHeader { #[derive(Debug, Deserialize)] struct V6Raw { nam_offset: u64, - seg_offset_zeroed: u64, + _seg_offset_zeroed: u64, til_offset: u64, initial_checksums: [u32; 3], - unk4_zeroed: [u8; 4], + _unk4_zeroed: [u8; 4], unk5_checksum: u32, id2_offset: u64, final_checksum: u32, - unk0_v7c: u32, + _unk0_v7c: u32, } let v6_raw: V6Raw = bincode::deserialize_from(input)?; let id0_offset = @@ -429,9 +438,12 @@ impl IDBHeader { let id1_offset = u64::from_le(u64::from(header_raw.offsets[3]) << 32 | u64::from(header_raw.offsets[2])); - ensure!(v6_raw.unk4_zeroed == [0; 4], "unk4 not zeroed"); - ensure!(v6_raw.seg_offset_zeroed == 0, "seg in V6 is not zeroed"); - ensure!(v6_raw.unk0_v7c == 0x7C, "unk0 not 0x7C"); + #[cfg(not(feature = "permissive"))] + { + ensure!(v6_raw._unk4_zeroed == [0; 4], "unk4 not zeroed"); + ensure!(v6_raw._seg_offset_zeroed == 0, "seg in V6 is not zeroed"); + ensure!(v6_raw._unk0_v7c == 0x7C, "unk0 not 0x7C"); + } // TODO ensure all offsets point to after the header Ok(Self { diff --git a/src/til.rs b/src/til.rs index 9ebee66..f8f2bf0 100644 --- a/src/til.rs +++ b/src/til.rs @@ -91,6 +91,7 @@ impl TILTypeInfo { .into_iter() .map(|field| if field.is_empty() { None } else { Some(field) }); let tinfo = Type::new(til, tinfo_raw, &mut fields_iter)?; + #[cfg(not(feature = "permissive"))] ensure!( fields_iter.next().is_none(), "Extra fields found for til type \"{}\"", @@ -186,10 +187,11 @@ impl Type { &[b'\x00'] => {} // in continuations, the \x00 may be missing &[] => {} - rest => { + _rest => { + #[cfg(not(feature = "permissive"))] return Err(anyhow!( "Extra {} bytes after reading TIL from ID0", - rest.len() + _rest.len() )); } } @@ -198,6 +200,7 @@ impl Type { .into_iter() .map(|field| if field.is_empty() { None } else { Some(field) }); let result = Self::new(&header, type_raw, &mut fields_iter)?; + #[cfg(not(feature = "permissive"))] ensure!( fields_iter.next().is_none(), "Extra fields found for id0 til" @@ -514,6 +517,7 @@ impl TILMacro { let have_param = flag & 0x100 != 0; let param_num = have_param.then_some((flag & 0xFF) as u8); if !have_param { + #[cfg(not(feature = "permissive"))] ensure!(flag & 0xFF == 0, "Unknown/Invalid value for TILMacro flag"); } // TODO find the InnerRef for this @@ -547,12 +551,14 @@ impl TILMacro { (_, None) => {} // having params, where should not (None, Some(_max)) => { + #[cfg(not(feature = "permissive"))] return Err(anyhow!( "Macro value have params but it is not declared in the flag", )) } // only using params that exist (Some(params), Some(max)) if max <= params => { + #[cfg(not(feature = "permissive"))] ensure!( max <= params, "Macro value have more params then declared in the flag" diff --git a/src/til/array.rs b/src/til/array.rs index 1383c24..a299f32 100644 --- a/src/til/array.rs +++ b/src/til/array.rs @@ -1,7 +1,5 @@ use std::num::{NonZeroU16, NonZeroU8}; -use anyhow::ensure; - use crate::ida_reader::IdaGenericBufUnpack; use crate::til::section::TILSectionHeader; use crate::til::{Type, TypeAttribute, TypeRaw}; @@ -58,15 +56,20 @@ impl ArrayRaw { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x48078e let align = match input.read_tah()? { None => None, - Some(TypeAttribute { tattr, extended }) => { + Some(TypeAttribute { + tattr, + extended: _extended, + }) => { let align = (tattr & MAX_DECL_ALIGN) as u8; - ensure!( + #[cfg(not(feature = "permissive"))] + anyhow::ensure!( tattr & !MAX_DECL_ALIGN == 0, "unknown TypeAttribute {tattr:x}" ); - ensure!( - extended.is_none(), - "unknown TypeAttribute ext {extended:x?}" + #[cfg(not(feature = "permissive"))] + anyhow::ensure!( + _extended.is_none(), + "unknown TypeAttribute ext {_extended:x?}" ); NonZeroU8::new(align) } diff --git a/src/til/bitfield.rs b/src/til/bitfield.rs index ba279c8..e1660ff 100644 --- a/src/til/bitfield.rs +++ b/src/til/bitfield.rs @@ -1,7 +1,11 @@ use std::num::NonZeroU8; +use anyhow::Result; + use crate::ida_reader::IdaGenericBufUnpack; +use super::TypeAttribute; + #[derive(Debug, Clone, Copy)] pub struct Bitfield { pub unsigned: bool, @@ -23,7 +27,7 @@ pub struct Bitfield { } impl Bitfield { - pub(crate) fn read(input: &mut impl IdaGenericBufUnpack, metadata: u8) -> anyhow::Result { + pub(crate) fn read(input: &mut impl IdaGenericBufUnpack, metadata: u8) -> Result { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x472f3c print_til_type let nbytes = match metadata { super::flag::tf_complex::BTMT_BFLDI8 => 1, @@ -35,7 +39,21 @@ impl Bitfield { let dt = input.read_dt()?; let width = dt >> 1; let unsigned = (dt & 1) > 0; - let _tah = input.read_tah()?; + match input.read_tah()? { + None => {} + Some(TypeAttribute { + tattr: _tattr, + extended: _extended, + }) => { + #[cfg(not(feature = "permissive"))] + anyhow::ensure!(_tattr == 0, "Unknown TypeAttribute {_tattr:x}"); + #[cfg(not(feature = "permissive"))] + anyhow::ensure!( + _extended.is_none(), + "Unknown TypeAttribute ext {_extended:x?}" + ); + } + } Ok(Self { unsigned, width, diff --git a/src/til/enum.rs b/src/til/enum.rs index 6f562ce..95c7c42 100644 --- a/src/til/enum.rs +++ b/src/til/enum.rs @@ -69,38 +69,39 @@ impl EnumRaw { return Ok(TypeVariantRaw::EnumRef(ref_type)); }; - let (is_64, is_signed, is_unsigned) = match input.read_tah()? { - None => (false, false, false), - Some(TypeAttribute { - tattr, - extended: None, - }) => { - // TODO enum have an align field (MAX_DECL_ALIGN) in tattr? - let is_64 = tattr & TAENUM_64BIT != 0; - let is_signed = tattr & TAENUM_SIGNED != 0; - let is_unsigned = tattr & TAENUM_UNSIGNED != 0; - ensure!( - tattr & !(TAENUM_64BIT | TAENUM_SIGNED | TAENUM_UNSIGNED) == 0, - "Invalid Enum taenum_bits {tattr:x}" - ); - ensure!( - !(is_signed && is_unsigned), - "Enum can't be signed and unsigned at the same time" - ); - (is_64, is_signed, is_unsigned) - } - // TODO parse ext attr? - Some(TypeAttribute { - tattr: _, - extended: Some(_), - }) => { - return Err(anyhow!("Unable to parse extended attributes for enum")); - } - }; + let mut is_64 = false; + let mut is_signed = false; + let mut is_unsigned = false; + if let Some(TypeAttribute { + tattr, + extended: _extended, + }) = input.read_sdacl()? + { + // TODO enum have an align field (MAX_DECL_ALIGN) in tattr? + is_64 = tattr & TAENUM_64BIT != 0; + is_signed = tattr & TAENUM_SIGNED != 0; + is_unsigned = tattr & TAENUM_UNSIGNED != 0; + #[cfg(not(feature = "permissive"))] + ensure!( + tattr & !(TAENUM_64BIT | TAENUM_SIGNED | TAENUM_UNSIGNED) == 0, + "Invalid Enum taenum_bits {tattr:x}" + ); + #[cfg(not(feature = "permissive"))] + ensure!( + !(is_signed && is_unsigned), + "Enum can't be signed and unsigned at the same time" + ); + #[cfg(not(feature = "permissive"))] + ensure!( + _extended.is_none(), + "Unable to parse extended attributes for Enum" + ); + } // all BTE bits are consumed let bte = input.read_u8()?; let storage_size_raw = bte & BTE_SIZE_MASK; + #[cfg(not(feature = "permissive"))] ensure!( bte & BTE_RESERVED == 0, "Enum BTE including the Always off sub-field" @@ -123,9 +124,10 @@ impl EnumRaw { // TODO enum size defaults to 4? let storage_size_final = storage_size.map(NonZeroU8::get).unwrap_or(4); let mask: u64 = if storage_size_final >= 16 { - // is saturating valid? - //u64::MAX + #[cfg(not(feature = "permissive"))] return Err(anyhow!("Bytes size is too big")); + #[cfg(feature = "permissive")] + u64::MAX } else { u64::MAX >> (u64::BITS - (storage_size_final as u32 * 8)) }; diff --git a/src/til/function.rs b/src/til/function.rs index 4bcde71..a45b749 100644 --- a/src/til/function.rs +++ b/src/til/function.rs @@ -152,6 +152,7 @@ impl FunctionRaw { let is_high = flags_lower & BFA_HIGH != 0; let is_static = flags_lower & BFA_STATIC != 0; let is_virtual = flags_lower & BFA_VIRTUAL != 0; + #[cfg(not(feature = "permissive"))] ensure!(flags_lower & !(BFA_NORET | BFA_PURE | BFA_HIGH | BFA_STATIC | BFA_VIRTUAL) == 0); // TODO find those flags @@ -162,6 +163,7 @@ impl FunctionRaw { let is_const = flags_upper & BFA_CONST != 0; let is_constructor = flags_upper & BFA_CONSTRUCTOR != 0; let is_destructor = flags_upper & BFA_DESTRUCTOR != 0; + #[cfg(not(feature = "permissive"))] ensure!(flags_upper & !(BFA_CONST | BFA_CONSTRUCTOR | BFA_DESTRUCTOR) == 0); let ret = TypeRaw::read(&mut *input, header).context("Return Argument")?; @@ -275,7 +277,10 @@ impl ArgLoc { let sval = input.read_de()?; Ok(Self::Static(sval)) } + #[cfg(not(feature = "permissive"))] ALOC_CUSTOM.. => Err(anyhow!("Custom implementation for ArgLoc")), + #[cfg(feature = "permissive")] + ALOC_CUSTOM.. => Ok(Self::None), } } } @@ -373,19 +378,19 @@ impl CCPtrSize { pub const fn near_bytes(self) -> NonZeroU8 { match self { - CCPtrSize::N8F16 => unsafe { NonZeroU8::new_unchecked(1) }, - CCPtrSize::N16F32 => unsafe { NonZeroU8::new_unchecked(2) }, - CCPtrSize::N32F48 => unsafe { NonZeroU8::new_unchecked(4) }, - CCPtrSize::N64 => unsafe { NonZeroU8::new_unchecked(8) }, + CCPtrSize::N8F16 => NonZeroU8::new(1).unwrap(), + CCPtrSize::N16F32 => NonZeroU8::new(2).unwrap(), + CCPtrSize::N32F48 => NonZeroU8::new(4).unwrap(), + CCPtrSize::N64 => NonZeroU8::new(8).unwrap(), } } pub const fn far_bytes(self) -> NonZeroU8 { match self { - CCPtrSize::N8F16 => unsafe { NonZeroU8::new_unchecked(2) }, - CCPtrSize::N16F32 => unsafe { NonZeroU8::new_unchecked(4) }, - CCPtrSize::N32F48 => unsafe { NonZeroU8::new_unchecked(6) }, - CCPtrSize::N64 => unsafe { NonZeroU8::new_unchecked(8) }, + CCPtrSize::N8F16 => NonZeroU8::new(2).unwrap(), + CCPtrSize::N16F32 => NonZeroU8::new(4).unwrap(), + CCPtrSize::N32F48 => NonZeroU8::new(6).unwrap(), + CCPtrSize::N64 => NonZeroU8::new(8).unwrap(), } } } @@ -516,9 +521,12 @@ fn read_cc_spoiled( } else { let size = (b >> 4) + 1; // TODO what if (b & 0xF) == 0? + #[cfg(not(feature = "permissive"))] let reg = (b & 0xF) .checked_sub(1) .ok_or_else(|| anyhow!("invalid spoiled reg value"))?; + #[cfg(feature = "permissive")] + let reg = (b & 0xF).saturating_sub(1); spoiled.push((reg.into(), size)) } } diff --git a/src/til/pointer.rs b/src/til/pointer.rs index 744e918..52ae45a 100644 --- a/src/til/pointer.rs +++ b/src/til/pointer.rs @@ -1,4 +1,4 @@ -use anyhow::{ensure, Result}; +use anyhow::Result; use crate::ida_reader::IdaGenericBufUnpack; use crate::til::section::TILSectionHeader; @@ -106,16 +106,20 @@ impl PointerRaw { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x459b7e let (_ta_lower, is_shifted, ptr_type_raw) = match input.read_tah()? { None => (0, false, 0), - Some(TypeAttribute { tattr, extended }) => { + Some(TypeAttribute { + tattr, + extended: _extended, + }) => { // all bits of tattr are consumed let ta_lower = (tattr & MAX_DECL_ALIGN) as u8; let is_shifted = tattr & TAPTR_SHIFTED != 0; let ptr_type = tattr & TAPTR_RESTRICT; - ensure!( + #[cfg(not(feature = "permissive"))] + anyhow::ensure!( tattr & !(TAPTR_SHIFTED | TAPTR_RESTRICT | MAX_DECL_ALIGN) == 0, "Invalid Pointer taenum_bits {tattr:x}" ); - if let Some(_extended) = extended { + if let Some(_extended) = _extended { // TODO parse extended values, known: // "__org_arrdim" :"\xac\xXX" // "__org_typedef":..., diff --git a/src/til/section.rs b/src/til/section.rs index 4b29bdd..342e508 100644 --- a/src/til/section.rs +++ b/src/til/section.rs @@ -285,7 +285,7 @@ impl TILSection { let size_long_double = header1 .flags .has_size_long_double() - .then(|| bincode::deserialize_from::<_, u8>(&mut *input)) + .then(|| input.read_u8()) .transpose()? .map(|size| size.try_into()) .transpose()?; @@ -468,6 +468,7 @@ impl TILSection { pub struct TILSectionFlags(pub(crate) u16); impl TILSectionFlags { fn new(value: u32) -> Result { + #[cfg(not(feature = "permissive"))] ensure!( value < (flag::til::TIL_SLD as u32) << 1, "Unknown flag values for TILSectionFlags" @@ -585,6 +586,7 @@ impl TILSection { next_ordinal, ordinal_alias, )?; + #[cfg(not(feature = "permissive"))] ensure!( compressed_input.limit() == 0, "TypeBucket compressed data is smaller then expected" @@ -613,6 +615,7 @@ impl TILSection { let type_info = (0..ndefs) .map(|i| TILTypeInfo::read(&mut input, header, i == ndefs - 1)) .collect::>()?; + #[cfg(not(feature = "permissive"))] ensure!( input.limit() == 0, "TypeBucket total data is smaller then expected" @@ -637,6 +640,7 @@ impl TILSection { let type_info = (0..ndefs) .map(|_| TILMacro::read(&mut input)) .collect::>()?; + #[cfg(not(feature = "permissive"))] ensure!( input.limit() == 0, "TypeBucket macro total data is smaller then expected" @@ -655,10 +659,12 @@ impl TILSection { .map(|_| TILMacro::read(&mut decompressed_input)) .collect::, _>>()?; // make sure the input was fully consumed + #[cfg(not(feature = "permissive"))] ensure!( decompressed_input.limit() == 0, "TypeBucket macros data is smaller then expected" ); + #[cfg(not(feature = "permissive"))] ensure!( compressed_input.limit() == 0, "TypeBucket macros compressed data is smaller then expected" @@ -678,10 +684,12 @@ impl TILSection { let inflate = flate2::read::ZlibDecoder::new(&mut compressed_input); let mut decompressed_input = inflate.take(len.into()); std::io::copy(&mut decompressed_input, output)?; + #[cfg(not(feature = "permissive"))] ensure!( decompressed_input.limit() == 0, "TypeBucket data is smaller then expected" ); + #[cfg(not(feature = "permissive"))] ensure!( compressed_input.limit() == 0, "TypeBucket compressed data is smaller then expected" diff --git a/src/til/struct.rs b/src/til/struct.rs index 1583d61..508f41e 100644 --- a/src/til/struct.rs +++ b/src/til/struct.rs @@ -99,7 +99,11 @@ impl StructRaw { let mut is_vft = false; let mut is_method = false; let mut is_bitset2 = false; - if let Some(TypeAttribute { tattr, extended }) = input.read_sdacl()? { + if let Some(TypeAttribute { + tattr, + extended: _extended, + }) = input.read_sdacl()? + { use crate::til::flag::tattr::*; use crate::til::flag::tattr_field::*; use crate::til::flag::tattr_udt::*; @@ -121,18 +125,20 @@ impl StructRaw { // TODO this value can't be right, it defines the alignment! is_bitset2 = align_raw & 0x4 != 0; - const ALL_FLAGS: u16 = MAX_DECL_ALIGN + const _ALL_FLAGS: u16 = MAX_DECL_ALIGN | TAUDT_MSSTRUCT | TAUDT_UNALIGNED | TAUDT_CPPOBJ | TAUDT_VFTABLE | TAFLD_METHOD; + #[cfg(not(feature = "permissive"))] ensure!( - tattr & !ALL_FLAGS == 0, + tattr & !_ALL_FLAGS == 0, "Invalid Struct taenum_bits {tattr:x}" ); + #[cfg(not(feature = "permissive"))] ensure!( - extended.is_none(), + _extended.is_none(), "Unable to parse extended attributes for struct" ); } @@ -223,7 +229,11 @@ impl StructMemberRaw { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x47825d if !is_bit_set || att.is_some() { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x47825d - if let Some(TypeAttribute { tattr, extended }) = input.read_sdacl()? { + if let Some(TypeAttribute { + tattr, + extended: _extended, + }) = input.read_sdacl()? + { use crate::til::flag::tattr::*; use crate::til::flag::tattr_field::*; @@ -235,17 +245,19 @@ impl StructMemberRaw { is_vft = tattr & TAFLD_VFTABLE != 0; // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x478203 is_method = tattr & TAFLD_METHOD != 0; - const ALL_FLAGS: u16 = MAX_DECL_ALIGN + const _ALL_FLAGS: u16 = MAX_DECL_ALIGN | TAFLD_BASECLASS | TAFLD_UNALIGNED | TAFLD_VFTABLE | TAFLD_METHOD; + #[cfg(not(feature = "permissive"))] ensure!( - tattr & !ALL_FLAGS == 0, + tattr & !_ALL_FLAGS == 0, "Invalid Struct taenum_bits {tattr:x}" ); + #[cfg(not(feature = "permissive"))] ensure!( - extended.is_none(), + _extended.is_none(), "Unable to parse extended attributes for struct member" ); } diff --git a/src/til/union.rs b/src/til/union.rs index af42b60..68a1012 100644 --- a/src/til/union.rs +++ b/src/til/union.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, ensure, Context}; +use anyhow::{anyhow, Context, Result}; use std::num::NonZeroU8; @@ -21,7 +21,7 @@ impl Union { til: &TILSectionHeader, value: UnionRaw, fields: &mut impl Iterator>>, - ) -> anyhow::Result { + ) -> Result { let members = value .members .into_iter() @@ -30,7 +30,7 @@ impl Union { let new_member = Type::new(til, member, &mut *fields)?; Ok((field_name, new_member)) }) - .collect::>()?; + .collect::>()?; Ok(Union { effective_alignment: value.effective_alignment, alignment: value.alignment, @@ -54,7 +54,7 @@ impl UnionRaw { pub fn read( input: &mut impl IdaGenericBufUnpack, header: &TILSectionHeader, - ) -> anyhow::Result { + ) -> Result { let Some(n) = input.read_dt_de()? else { // InnerRef fb47f2c2-3c08-4d40-b7ab-3c7736dce31d 0x4803b4 // is ref @@ -73,27 +73,33 @@ impl UnionRaw { let mut alignment = None; let mut is_unaligned = false; - if let Some(TypeAttribute { tattr, extended }) = input.read_sdacl()? { + if let Some(TypeAttribute { + tattr, + extended: _extended, + }) = input.read_sdacl()? + { use crate::til::flag::tattr::*; use crate::til::flag::tattr_udt::*; alignment = NonZeroU8::new((tattr & MAX_DECL_ALIGN) as u8); is_unaligned = tattr & TAUDT_UNALIGNED != 0; - const ALL_FLAGS: u16 = MAX_DECL_ALIGN | TAUDT_UNALIGNED; - ensure!( - tattr & !ALL_FLAGS == 0, + const _ALL_FLAGS: u16 = MAX_DECL_ALIGN | TAUDT_UNALIGNED; + #[cfg(not(feature = "permissive"))] + anyhow::ensure!( + tattr & !_ALL_FLAGS == 0, "Invalid Union taenum_bits {tattr:x}" ); - ensure!( - extended.is_none(), + #[cfg(not(feature = "permissive"))] + anyhow::ensure!( + _extended.is_none(), "Unable to parse extended attributes for union" ); } let members = (0..mem_cnt) .map(|i| TypeRaw::read(&mut *input, header).with_context(|| format!("Member {i}"))) - .collect::>()?; + .collect::>()?; Ok(TypeVariantRaw::Union(Self { effective_alignment, alignment,