use crate::{validation::ArchiveContext, Fallible};
use core::{alloc::Layout, fmt, ops::Range};
#[derive(Debug)]
pub enum ArchiveError {
Overflow {
base: *const u8,
offset: isize,
},
Underaligned {
expected_align: usize,
actual_align: usize,
},
OutOfBounds {
base: *const u8,
offset: isize,
range: Range<*const u8>,
},
Overrun {
ptr: *const u8,
size: usize,
range: Range<*const u8>,
},
Unaligned {
ptr: *const u8,
align: usize,
},
SubtreePointerOutOfBounds {
ptr: *const u8,
subtree_range: Range<*const u8>,
},
SubtreePointerOverrun {
ptr: *const u8,
size: usize,
subtree_range: Range<*const u8>,
},
RangePoppedOutOfOrder {
expected_depth: usize,
actual_depth: usize,
},
UnpoppedSubtreeRanges {
last_range: usize,
},
ExceededMaximumSubtreeDepth {
max_subtree_depth: usize,
},
}
unsafe impl Send for ArchiveError {}
unsafe impl Sync for ArchiveError {}
impl fmt::Display for ArchiveError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ArchiveError::Overflow { base, offset } => write!(
f,
"relative pointer overflowed: base {:p} offset {}",
base, offset
),
ArchiveError::Underaligned {
expected_align,
actual_align,
} => write!(
f,
"archive underaligned: need alignment {} but have alignment {}",
expected_align, actual_align
),
ArchiveError::OutOfBounds {
base,
offset,
range,
} => write!(
f,
"pointer out of bounds: base {:p} offset {} not in range {:p}..{:p}",
base, offset, range.start, range.end
),
ArchiveError::Overrun { ptr, size, range } => write!(
f,
"pointer overran buffer: ptr {:p} size {} in range {:p}..{:p}",
ptr, size, range.start, range.end
),
ArchiveError::Unaligned { ptr, align } => write!(
f,
"unaligned pointer: ptr {:p} unaligned for alignment {}",
ptr, align
),
ArchiveError::SubtreePointerOutOfBounds { ptr, subtree_range } => write!(
f,
"subtree pointer out of bounds: ptr {:p} not in range {:p}..{:p}",
ptr, subtree_range.start, subtree_range.end
),
ArchiveError::SubtreePointerOverrun {
ptr,
size,
subtree_range,
} => write!(
f,
"subtree pointer overran range: ptr {:p} size {} in range {:p}..{:p}",
ptr, size, subtree_range.start, subtree_range.end
),
ArchiveError::RangePoppedOutOfOrder {
expected_depth,
actual_depth,
} => write!(
f,
"subtree range popped out of order: expected depth {}, actual depth {}",
expected_depth, actual_depth
),
ArchiveError::UnpoppedSubtreeRanges { last_range } => {
write!(f, "unpopped subtree ranges: last range {}", last_range)
}
ArchiveError::ExceededMaximumSubtreeDepth { max_subtree_depth } => write!(
f,
"pushed a subtree range that exceeded the maximum subtree depth of {}",
max_subtree_depth
),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ArchiveError {}
#[derive(Debug)]
pub struct PrefixRange {
range: Range<*const u8>,
depth: usize,
}
unsafe impl<'a> Send for PrefixRange {}
unsafe impl Sync for PrefixRange {}
#[derive(Debug)]
pub struct SuffixRange {
start: *const u8,
depth: usize,
}
unsafe impl<'a> Send for SuffixRange {}
unsafe impl Sync for SuffixRange {}
#[derive(Debug)]
pub struct ArchiveValidator<'a> {
bytes: &'a [u8],
subtree_range: Range<*const u8>,
subtree_depth: usize,
max_subtree_depth: usize,
}
unsafe impl<'a> Send for ArchiveValidator<'a> {}
unsafe impl<'a> Sync for ArchiveValidator<'a> {}
impl<'a> ArchiveValidator<'a> {
#[inline]
pub fn new(bytes: &'a [u8]) -> Self {
Self::with_max_depth(bytes, usize::MAX)
}
#[inline]
pub fn with_max_depth(bytes: &'a [u8], max_subtree_depth: usize) -> Self {
Self {
bytes,
subtree_range: bytes.as_ptr_range(),
subtree_depth: 0,
max_subtree_depth,
}
}
#[inline]
pub fn log_alignment(&self) -> usize {
(self.bytes.as_ptr() as usize).trailing_zeros() as usize
}
#[inline]
pub fn alignment(&self) -> usize {
1 << self.log_alignment()
}
}
impl<'a> Fallible for ArchiveValidator<'a> {
type Error = ArchiveError;
}
impl<'a> ArchiveContext for ArchiveValidator<'a> {
type PrefixRange = PrefixRange;
type SuffixRange = SuffixRange;
#[inline]
unsafe fn bounds_check_ptr(
&mut self,
base: *const u8,
offset: isize,
) -> Result<*const u8, Self::Error> {
let base_pos = base.offset_from(self.bytes.as_ptr());
let target_pos = base_pos
.checked_add(offset)
.ok_or(ArchiveError::Overflow { base, offset })?;
if target_pos < 0 || target_pos as usize > self.bytes.len() {
Err(ArchiveError::OutOfBounds {
base,
offset,
range: self.bytes.as_ptr_range(),
})
} else {
Ok(base.offset(offset))
}
}
#[inline]
unsafe fn bounds_check_layout(
&mut self,
data_address: *const u8,
layout: &Layout,
) -> Result<(), Self::Error> {
if self.alignment() < layout.align() {
Err(ArchiveError::Underaligned {
expected_align: layout.align(),
actual_align: self.alignment(),
})
} else if (data_address as usize) & (layout.align() - 1) != 0 {
Err(ArchiveError::Unaligned {
ptr: data_address,
align: layout.align(),
})
} else {
let available_space = self.bytes.as_ptr_range().end.offset_from(data_address) as usize;
if available_space < layout.size() {
Err(ArchiveError::Overrun {
ptr: data_address,
size: layout.size(),
range: self.bytes.as_ptr_range(),
})
} else {
Ok(())
}
}
}
#[inline]
unsafe fn bounds_check_subtree_ptr_layout(
&mut self,
data_address: *const u8,
layout: &Layout,
) -> Result<(), Self::Error> {
if layout.size() == 0 {
if data_address < self.subtree_range.start || data_address > self.subtree_range.end {
Err(ArchiveError::SubtreePointerOutOfBounds {
ptr: data_address,
subtree_range: self.subtree_range.clone(),
})
} else {
Ok(())
}
} else if !self.subtree_range.contains(&data_address) {
Err(ArchiveError::SubtreePointerOutOfBounds {
ptr: data_address,
subtree_range: self.subtree_range.clone(),
})
} else {
let available_space = self.subtree_range.end.offset_from(data_address) as usize;
if available_space < layout.size() {
Err(ArchiveError::SubtreePointerOverrun {
ptr: data_address,
size: layout.size(),
subtree_range: self.subtree_range.clone(),
})
} else {
Ok(())
}
}
}
#[inline]
unsafe fn push_prefix_subtree_range(
&mut self,
root: *const u8,
end: *const u8,
) -> Result<PrefixRange, Self::Error> {
if self.subtree_depth >= self.max_subtree_depth {
Err(ArchiveError::ExceededMaximumSubtreeDepth {
max_subtree_depth: self.max_subtree_depth,
})
} else {
let result = PrefixRange {
range: Range {
start: end,
end: self.subtree_range.end,
},
depth: self.subtree_depth,
};
self.subtree_depth += 1;
self.subtree_range.end = root;
Ok(result)
}
}
#[inline]
fn pop_prefix_range(&mut self, range: PrefixRange) -> Result<(), Self::Error> {
if self.subtree_depth - 1 != range.depth {
Err(ArchiveError::RangePoppedOutOfOrder {
expected_depth: self.subtree_depth - 1,
actual_depth: range.depth,
})
} else {
self.subtree_range = range.range;
self.subtree_depth = range.depth;
Ok(())
}
}
#[inline]
unsafe fn push_suffix_subtree_range(
&mut self,
start: *const u8,
root: *const u8,
) -> Result<SuffixRange, Self::Error> {
let result = SuffixRange {
start: self.subtree_range.start,
depth: self.subtree_depth,
};
self.subtree_depth += 1;
self.subtree_range.start = start;
self.subtree_range.end = root;
Ok(result)
}
#[inline]
fn pop_suffix_range(&mut self, range: SuffixRange) -> Result<(), Self::Error> {
if self.subtree_depth - 1 != range.depth {
Err(ArchiveError::RangePoppedOutOfOrder {
expected_depth: self.subtree_depth - 1,
actual_depth: range.depth,
})
} else {
self.subtree_range.end = self.subtree_range.start;
self.subtree_range.start = range.start;
self.subtree_depth = range.depth;
Ok(())
}
}
#[inline]
fn finish(&mut self) -> Result<(), Self::Error> {
if self.subtree_depth != 0 {
Err(ArchiveError::UnpoppedSubtreeRanges {
last_range: self.subtree_depth - 1,
})
} else {
Ok(())
}
}
}