Skip to content

Commit

Permalink
proper bounds checks for mem-reserve/struct blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
kadiwa4 committed Apr 1, 2024
1 parent e90e8a4 commit 6c05e19
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 78 deletions.
34 changes: 21 additions & 13 deletions src/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,15 +227,14 @@ impl Devicetree {
/// - header fields `off_dt_strings` and `size_dt_strings` can be used to obtain an in-bounds
/// block
fn late_checks(&self) -> Result<()> {
let header = self.header();
if header.last_comp_version != LAST_COMPATIBLE_VERSION.to_be() {
if self.header().last_comp_version != LAST_COMPATIBLE_VERSION.to_be() {
return Err(BlobError::IncompatibleVersion);
}

let exact_size = self.exact_size() as usize;
let (offset, size) = Option::zip(
usize::try_from(u32::from_be(header.off_dt_struct)).ok(),
usize::try_from(u32::from_be(header.size_dt_struct)).ok(),
usize::try_from(u32::from_be(self.header().off_dt_struct)).ok(),
usize::try_from(u32::from_be(self.header().size_dt_struct)).ok(),
)
.filter(|&(o, s)| usize::checked_add(o, s).is_some_and(|e| e <= exact_size))
.ok_or(BlobError::BlockOutOfBounds)?;
Expand All @@ -245,8 +244,8 @@ impl Devicetree {
}

if !Option::zip(
usize::try_from(u32::from_be(header.off_dt_strings)).ok(),
usize::try_from(u32::from_be(header.size_dt_strings)).ok(),
usize::try_from(u32::from_be(self.header().off_dt_strings)).ok(),
usize::try_from(u32::from_be(self.header().size_dt_strings)).ok(),
)
.and_then(|(o, s)| usize::checked_add(o, s))
.is_some_and(|e| e <= exact_size)
Expand Down Expand Up @@ -311,9 +310,8 @@ impl Devicetree {

/// The blob data of the struct block.
pub fn struct_blob(&self) -> &[u32] {
let header = self.header();
let offset = u32::from_be(header.off_dt_struct) as usize;
let len = u32::from_be(header.size_dt_struct) as usize;
let offset = u32::from_be(self.header().off_dt_struct) as usize;
let len = u32::from_be(self.header().size_dt_struct) as usize;

// SAFETY: type guarantees that the block is in-bounds
unsafe {
Expand All @@ -325,11 +323,19 @@ impl Devicetree {
}
}

/// The devicetree blob, except it ends where the struct block ends.
fn blob_with_struct_block_end(&self) -> &[u8] {
let offset = u32::from_be(self.header().off_dt_struct) as usize;
let len = u32::from_be(self.header().size_dt_struct) as usize;

// SAFETY: type guarantees that the block is in-bounds
unsafe { self.blob_u8().get_unchecked(..offset + len) }
}

/// The blob data of the strings block.
pub fn strings_blob(&self) -> &[u8] {
let header = self.header();
let offset = u32::from_be(header.off_dt_strings) as usize;
let len = u32::from_be(header.size_dt_strings) as usize;
let offset = u32::from_be(self.header().off_dt_strings) as usize;
let len = u32::from_be(self.header().size_dt_strings) as usize;
// SAFETY: type guarantees that the block is in-bounds
unsafe { crate::util::slice_get_with_len_unchecked(self.blob_u8(), offset, len) }
}
Expand Down Expand Up @@ -383,10 +389,12 @@ impl Devicetree {
}

let offset = usize::try_from(offset).map_err(|_| BlobError::BlockOutOfBounds)?;
// type guarantees that `totalsize` is valid and the struct block is in-bounds
let end_offset = u32::from_be(self.header().off_dt_struct) as usize;
Ok(MemReserveEntries {
blob: self
.blob
.get(offset / DTB_OPTIMAL_ALIGN..)
.get(offset / DTB_OPTIMAL_ALIGN..end_offset / DTB_OPTIMAL_ALIGN)
.ok_or(BlobError::BlockOutOfBounds)?,
})
}
Expand Down
55 changes: 27 additions & 28 deletions src/blob/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ impl Devicetree {
nameoff: u32,
}

let blob = self.blob_u8();
let blob = self.blob_with_struct_block_end();
loop {
let token = self.next_raw(cursor)?.ok_or(BlobError::UnexpectedEnd)?;
let token = next_raw(blob, cursor)?.ok_or(BlobError::UnexpectedEnd)?;
let offset = cursor.offset as usize;

let token = match token {
Expand All @@ -153,7 +153,7 @@ impl Devicetree {
}
RawToken::Prop => {
let header = PropHeader::read_from_prefix(&blob[offset..])
.ok_or(BlobError::InvalidPropertyHeader)?;
.ok_or(BlobError::UnexpectedEnd)?;

let name_blob = usize::try_from(u32::from_be(header.nameoff))
.ok()
Expand Down Expand Up @@ -184,30 +184,29 @@ impl Devicetree {
return Ok(Some(token));
}
}
}

fn next_raw(&self, cursor: &mut Cursor) -> Result<Option<RawToken>> {
const BEGIN_NODE: u32 = RawToken::BeginNode as u32;
const END_NODE: u32 = RawToken::EndNode as u32;
const PROP: u32 = RawToken::Prop as u32;
const NOP: u32 = RawToken::Nop as u32;
const END: u32 = RawToken::End as u32;

let offset = cursor.offset as usize;
let Some(token) = util::slice_get_with_len(self.blob_u8(), offset, TOKEN_SIZE as usize)
else {
return Ok(None);
};
let token = u32::from_ne_bytes(token.try_into().unwrap());

cursor.offset += TOKEN_SIZE;
let token = match token {
BEGIN_NODE => RawToken::BeginNode,
END_NODE => RawToken::EndNode,
PROP => RawToken::Prop,
NOP => RawToken::Nop,
END => RawToken::End,
_ => return Err(BlobError::UnknownToken),
};
Ok(Some(token))
}
fn next_raw(blob: &[u8], cursor: &mut Cursor) -> Result<Option<RawToken>> {
const BEGIN_NODE: u32 = RawToken::BeginNode as u32;
const END_NODE: u32 = RawToken::EndNode as u32;
const PROP: u32 = RawToken::Prop as u32;
const NOP: u32 = RawToken::Nop as u32;
const END: u32 = RawToken::End as u32;

let offset = cursor.offset as usize;
let Some(token) = util::slice_get_with_len(blob, offset, TOKEN_SIZE as usize) else {
return Ok(None);
};
let token = u32::from_ne_bytes(token.try_into().unwrap());

cursor.offset += TOKEN_SIZE;
let token = match token {
BEGIN_NODE => RawToken::BeginNode,
END_NODE => RawToken::EndNode,
PROP => RawToken::Prop,
NOP => RawToken::Nop,
END => RawToken::End,
_ => return Err(BlobError::UnknownToken),
};
Ok(Some(token))
}
Loading

0 comments on commit 6c05e19

Please sign in to comment.