use core::{
alloc::Layout, error::Error, fmt, marker::PhantomData, num::NonZeroUsize,
ops::Range,
};
use rancor::{fail, OptionExt, Source};
use crate::validation::ArchiveContext;
const PTR_WIDTH: usize = (usize::BITS / 4 + 2) as usize;
struct Pointer(pub usize);
impl fmt::Display for Pointer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#0w$x}", self.0, w = PTR_WIDTH)
}
}
#[derive(Debug)]
struct UnalignedPointer {
address: usize,
align: usize,
}
impl fmt::Display for UnalignedPointer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"unaligned pointer: ptr {} unaligned for alignment {}",
Pointer(self.address),
self.align,
)
}
}
impl Error for UnalignedPointer {}
#[derive(Debug)]
struct InvalidSubtreePointer {
address: usize,
size: usize,
subtree_range: Range<usize>,
}
impl fmt::Display for InvalidSubtreePointer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"subtree pointer overran range: ptr {} size {} in range {}..{}",
Pointer(self.address),
self.size,
Pointer(self.subtree_range.start),
Pointer(self.subtree_range.end),
)
}
}
impl Error for InvalidSubtreePointer {}
#[derive(Debug)]
struct ExceededMaximumSubtreeDepth;
impl fmt::Display for ExceededMaximumSubtreeDepth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"pushed a subtree range that exceeded the maximum subtree depth",
)
}
}
impl Error for ExceededMaximumSubtreeDepth {}
#[derive(Debug)]
struct RangePoppedTooManyTimes;
impl fmt::Display for RangePoppedTooManyTimes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "subtree range popped too many times")
}
}
impl Error for RangePoppedTooManyTimes {}
#[derive(Debug)]
struct RangePoppedOutOfOrder;
impl fmt::Display for RangePoppedOutOfOrder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "subtree range popped out of order")
}
}
impl Error for RangePoppedOutOfOrder {}
#[derive(Debug)]
pub struct ArchiveValidator<'a> {
subtree_range: Range<usize>,
max_subtree_depth: Option<NonZeroUsize>,
_phantom: PhantomData<&'a [u8]>,
}
impl<'a> ArchiveValidator<'a> {
#[inline]
pub fn new(bytes: &'a [u8]) -> Self {
Self::with_max_depth(bytes, None)
}
#[inline]
pub fn with_max_depth(
bytes: &'a [u8],
max_subtree_depth: Option<NonZeroUsize>,
) -> Self {
let Range { start, end } = bytes.as_ptr_range();
Self {
subtree_range: Range {
start: start as usize,
end: end as usize,
},
max_subtree_depth,
_phantom: PhantomData,
}
}
}
unsafe impl<E: Source> ArchiveContext<E> for ArchiveValidator<'_> {
fn check_subtree_ptr(
&mut self,
ptr: *const u8,
layout: &Layout,
) -> Result<(), E> {
let start = ptr as usize;
let end = ptr.wrapping_add(layout.size()) as usize;
if end < start
|| start < self.subtree_range.start
|| end > self.subtree_range.end
{
fail!(InvalidSubtreePointer {
address: start,
size: layout.size(),
subtree_range: self.subtree_range.clone(),
});
} else if start & (layout.align() - 1) != 0 {
fail!(UnalignedPointer {
address: ptr as usize,
align: layout.align(),
});
} else {
Ok(())
}
}
unsafe fn push_subtree_range(
&mut self,
root: *const u8,
end: *const u8,
) -> Result<Range<usize>, E> {
if let Some(max_subtree_depth) = &mut self.max_subtree_depth {
*max_subtree_depth = NonZeroUsize::new(max_subtree_depth.get() - 1)
.into_trace(ExceededMaximumSubtreeDepth)?;
}
let result = Range {
start: end as usize,
end: self.subtree_range.end,
};
self.subtree_range.end = root as usize;
Ok(result)
}
unsafe fn pop_subtree_range(
&mut self,
range: Range<usize>,
) -> Result<(), E> {
if range.start < self.subtree_range.end {
fail!(RangePoppedOutOfOrder);
}
self.subtree_range = range;
if let Some(max_subtree_depth) = &mut self.max_subtree_depth {
*max_subtree_depth = max_subtree_depth
.checked_add(1)
.into_trace(RangePoppedTooManyTimes)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use rancor::Error;
use crate::{
api::low::{access, access_pos},
boxed::ArchivedBox,
option::ArchivedOption,
util::Align,
Archived,
};
#[test]
fn basic_functionality() {
#[cfg(all(feature = "pointer_width_16", not(feature = "big_endian")))]
let synthetic_buf = Align([
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0u8, 1u8, 0u8, 0xf2u8, 0xffu8, 11u8, 0u8, ]);
#[cfg(all(feature = "pointer_width_16", feature = "big_endian"))]
let synthetic_buf = Align([
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0u8, 1u8, 0u8, 0xffu8, 0xf2u8, 0u8, 11u8, ]);
#[cfg(all(
not(any(
feature = "pointer_width_16",
feature = "pointer_width_64",
)),
not(feature = "big_endian"),
))]
let synthetic_buf = Align([
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0u8, 1u8, 0u8, 0u8, 0u8, 0xf0u8, 0xffu8, 0xffu8, 0xffu8, 11u8, 0u8, 0u8, 0u8, ]);
#[cfg(all(
not(any(
feature = "pointer_width_16",
feature = "pointer_width_64",
)),
feature = "big_endian",
))]
let synthetic_buf = Align([
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0u8, 1u8, 0u8, 0u8, 0u8, 0xffu8, 0xffu8, 0xffu8, 0xf0u8, 0u8, 0u8, 0u8, 11u8, ]);
#[cfg(all(feature = "pointer_width_64", not(feature = "big_endian")))]
let synthetic_buf = Align([
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0xe8u8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8,
11u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
0u8, ]);
#[cfg(all(feature = "pointer_width_64", feature = "big_endian"))]
let synthetic_buf = Align([
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0x21, 0x21, 0x21, 0x21, 0x21, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
0u8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xe8u8, 0u8,
0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
11u8, ]);
let result =
access::<ArchivedOption<ArchivedBox<[u8]>>, Error>(&*synthetic_buf);
result.unwrap();
let result =
access_pos::<Archived<u32>, Error>(&*Align([0, 1, 2, 3, 4]), 8);
assert_source!(
result.unwrap_err(),
super::InvalidSubtreePointer { size: 4, .. },
"error source should be out-of-bounds",
);
let result =
access_pos::<Archived<u32>, Error>(&*Align([0, 1, 2, 3, 4]), 4);
assert_source!(
result.unwrap_err(),
super::InvalidSubtreePointer { size: 4, .. },
"error source should be overrun",
);
let result =
access_pos::<Archived<u32>, Error>(&*Align([0, 1, 2, 3, 4]), 1);
assert_source!(
result.unwrap_err(),
super::UnalignedPointer { align: 4, .. },
"error source should be unaligned",
);
}
#[cfg(not(any(
feature = "pointer_width_16",
feature = "pointer_width_64"
)))]
#[test]
fn invalid_tags() {
let synthetic_buf = Align([
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0u8, 2u8, 0u8, 0u8, 0u8, 0xe8, 0xff, 0xff, 0xff, 11u8, 0u8, 0u8, 0u8, ]);
let result =
access::<ArchivedOption<ArchivedBox<[u8]>>, Error>(&*synthetic_buf);
assert_source!(
result.unwrap_err(),
bytecheck::InvalidEnumDiscriminantError::<u8> {
enum_name: "ArchivedOption",
invalid_discriminant: 2,
},
"error source should be invalid enum discriminant",
);
}
#[cfg(not(any(
feature = "pointer_width_16",
feature = "pointer_width_64"
)))]
#[test]
fn overlapping_claims() {
let synthetic_buf = Align([
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0, 0xf0, 0xff, 0xff, 0xff, 11u8, 0u8, 0u8, 0u8, 0xe8, 0xff, 0xff, 0xff, 11u8, 0u8, 0u8, 0u8, ]);
let result = access::<[ArchivedBox<[u8]>; 2], Error>(&*synthetic_buf);
assert_source!(
result.unwrap_err(),
super::InvalidSubtreePointer { size: 11, .. },
"error source should be invalid subtree pointer",
);
}
#[cfg(not(any(
feature = "pointer_width_16",
feature = "pointer_width_64"
)))]
#[cfg(feature = "alloc")]
#[test]
fn cycle_detection() {
use crate::{
ser::Writer, validation::ArchiveContext, Archive, Serialize,
};
#[allow(dead_code)]
#[derive(Archive, Serialize)]
#[rkyv(
crate,
serialize_bounds(__S: Writer),
bytecheck(bounds(__C: ArchiveContext)),
derive(Debug),
)]
enum Node {
Nil,
Cons(#[rkyv(omit_bounds)] crate::alloc::boxed::Box<Node>),
}
let synthetic_buf = Align([
1u8, 0u8, 0u8, 0u8, 4u8, 0u8, 0u8, 0u8, 1u8, 0u8, 0u8, 0u8, 244u8, 255u8, 255u8, 255u8, ]);
let result = access::<ArchivedNode, Error>(&*synthetic_buf);
assert_source!(
result.unwrap_err(),
super::InvalidSubtreePointer { .. },
"error source should be invalid subtree pointer",
);
}
}