pub mod archive;
pub mod shared;
use core::{any::TypeId, ops::Range};
pub use self::{
archive::{ArchiveContext, ArchiveContextExt},
shared::SharedContext,
};
#[derive(Debug)]
pub struct Validator<A, S> {
archive: A,
shared: S,
}
impl<A, S> Validator<A, S> {
#[inline]
pub fn new(archive: A, shared: S) -> Self {
Self { archive, shared }
}
}
unsafe impl<A, S, E> ArchiveContext<E> for Validator<A, S>
where
A: ArchiveContext<E>,
{
fn check_subtree_ptr(
&mut self,
ptr: *const u8,
layout: &core::alloc::Layout,
) -> Result<(), E> {
self.archive.check_subtree_ptr(ptr, layout)
}
unsafe fn push_subtree_range(
&mut self,
root: *const u8,
end: *const u8,
) -> Result<Range<usize>, E> {
unsafe { self.archive.push_subtree_range(root, end) }
}
unsafe fn pop_subtree_range(
&mut self,
range: Range<usize>,
) -> Result<(), E> {
unsafe { self.archive.pop_subtree_range(range) }
}
}
impl<A, S, E> SharedContext<E> for Validator<A, S>
where
S: SharedContext<E>,
{
fn start_shared(
&mut self,
address: usize,
type_id: TypeId,
) -> Result<shared::ValidationState, E> {
self.shared.start_shared(address, type_id)
}
fn finish_shared(
&mut self,
address: usize,
type_id: TypeId,
) -> Result<(), E> {
self.shared.finish_shared(address, type_id)
}
}
#[cfg(test)]
mod tests {
use rancor::Failure;
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]>>, Failure>(
&*synthetic_buf,
);
result.unwrap();
access_pos::<Archived<u32>, Failure>(&*Align([0, 1, 2, 3, 4]), 8)
.expect_err("expected out of bounds error");
access_pos::<Archived<u32>, Failure>(&*Align([0, 1, 2, 3, 4]), 4)
.expect_err("expected overrun error");
access_pos::<Archived<u32>, Failure>(&*Align([0, 1, 2, 3, 4]), 1)
.expect_err("expected unaligned error");
access_pos::<Archived<u32>, Failure>(&Align([0, 1, 2, 3, 4])[1..], 0)
.expect_err("expected underaligned error");
access::<Archived<u32>, Failure>(&*Align([]))
.expect_err("expected out of bounds error");
}
#[cfg(feature = "pointer_width_32")]
#[test]
fn invalid_tags() {
let synthetic_buf = Align([
2u8, 0u8, 0u8, 0u8, 8u8, 0u8, 0u8, 0u8, 11u8, 0u8, 0u8, 0u8, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
]);
let result = access_pos::<Archived<Option<Box<[u8]>>>, Failure>(
&*synthetic_buf,
0,
);
result.unwrap_err();
}
#[cfg(feature = "pointer_width_32")]
#[test]
fn overlapping_claims() {
let synthetic_buf = Align([
16u8, 0u8, 0u8, 0u8, 11u8, 0u8, 0u8, 0u8, 8u8, 0u8, 0u8, 0u8, 11u8, 0u8, 0u8, 0u8, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
]);
access_pos::<Archived<[Box<[u8]>; 2]>, Failure>(&*synthetic_buf, 0)
.unwrap_err();
}
#[cfg(feature = "pointer_width_32")]
#[test]
fn cycle_detection() {
use bytecheck::CheckBytes;
use rancor::{Fallible, Source};
use crate::{
ser::Writer, validation::ArchiveContext, Archive, Serialize,
};
#[allow(dead_code)]
#[derive(Archive)]
#[rkyv(crate, derive(Debug))]
enum Node {
Nil,
Cons(#[omit_bounds] Box<Node>),
}
impl<S: Fallible + Writer + ?Sized> Serialize<S> for Node {
fn serialize(
&self,
serializer: &mut S,
) -> Result<NodeResolver, S::Error> {
Ok(match self {
Node::Nil => NodeResolver::Nil,
Node::Cons(inner) => {
NodeResolver::Cons(inner.serialize(serializer)?)
}
})
}
}
unsafe impl<C> CheckBytes<C> for ArchivedNode
where
C: Fallible + ArchiveContext + ?Sized,
C::Error: Source,
{
unsafe fn check_bytes(
value: *const Self,
context: &mut C,
) -> Result<(), C::Error> {
let bytes = value.cast::<u8>();
let tag = unsafe { *bytes };
match tag {
0 => (),
1 => unsafe {
<Archived<Box<Node>> as CheckBytes<C>>::check_bytes(
bytes.add(4).cast(),
context,
)?;
},
_ => panic!(),
}
Ok(())
}
}
let synthetic_buf = Align([
1u8, 0u8, 0u8, 0u8, 4u8, 0u8, 0u8, 0u8, 1u8, 0u8, 0u8, 0u8, 244u8, 255u8, 255u8, 255u8, ]);
access_pos::<ArchivedNode, Failure>(&*synthetic_buf, 0).unwrap_err();
}
}