Skip to main content

rkyv/validation/archive/
mod.rs

1//! Basic archive buffer validation.
2
3mod validator;
4
5use core::{alloc::Layout, ops::Range};
6
7use bytecheck::rancor::{Fallible, Source, Strategy};
8use rancor::ResultExt as _;
9
10pub use self::validator::*;
11use crate::traits::LayoutRaw;
12
13/// A context that can validate nonlocal archive memory.
14///
15/// # Safety
16///
17/// `check_subtree_ptr` must only return true if `ptr` is located entirely
18/// within the subtree range and is safe to dereference.
19pub unsafe trait ArchiveContext<E = <Self as Fallible>::Error> {
20    /// Checks that the given data address and layout is located completely
21    /// within the subtree range.
22    fn check_subtree_ptr(
23        &mut self,
24        ptr: *const u8,
25        layout: &Layout,
26    ) -> Result<(), E>;
27
28    /// Pushes a new subtree range onto the validator and starts validating it.
29    ///
30    /// After calling `push_subtree_range`, the validator will have a subtree
31    /// range starting at the original start and ending at `root`. After popping
32    /// the returned range, the validator will have a subtree range starting at
33    /// `end` and ending at the original end.
34    ///
35    /// # Safety
36    ///
37    /// `root` and `end` must be located inside the archive.
38    unsafe fn push_subtree_range(
39        &mut self,
40        root: *const u8,
41        end: *const u8,
42    ) -> Result<Range<usize>, E>;
43
44    /// Pops the given range, restoring the original state with the pushed range
45    /// removed.
46    ///
47    /// If the range was not popped in reverse order, an error is returned.
48    ///
49    /// # Safety
50    ///
51    /// `range` must be a range returned from this validator.
52    unsafe fn pop_subtree_range(
53        &mut self,
54        range: Range<usize>,
55    ) -> Result<(), E>;
56}
57
58unsafe impl<T, E> ArchiveContext<E> for Strategy<T, E>
59where
60    T: ArchiveContext<E> + ?Sized,
61{
62    fn check_subtree_ptr(
63        &mut self,
64        ptr: *const u8,
65        layout: &Layout,
66    ) -> Result<(), E> {
67        T::check_subtree_ptr(self, ptr, layout)
68    }
69
70    unsafe fn push_subtree_range(
71        &mut self,
72        root: *const u8,
73        end: *const u8,
74    ) -> Result<Range<usize>, E> {
75        // SAFETY: This just forwards the call to the underlying context, which
76        // has the same safety requirements.
77        unsafe { T::push_subtree_range(self, root, end) }
78    }
79
80    unsafe fn pop_subtree_range(
81        &mut self,
82        range: Range<usize>,
83    ) -> Result<(), E> {
84        // SAFETY: This just forwards the call to the underlying context, which
85        // has the same safety requirements.
86        unsafe { T::pop_subtree_range(self, range) }
87    }
88}
89
90/// Helper methods for [`ArchiveContext`].
91pub trait ArchiveContextExt<E>: ArchiveContext<E> {
92    /// Checks that the given pointer and layout are within the current subtree
93    /// range of the context, then pushes a new subtree range onto the validator
94    /// for it and calls the given function.
95    fn in_subtree_raw<R>(
96        &mut self,
97        ptr: *const u8,
98        layout: Layout,
99        f: impl FnOnce(&mut Self) -> Result<R, E>,
100    ) -> Result<R, E>;
101
102    /// Checks that the value the given pointer points to is within the current
103    /// subtree range of the context, then pushes a new subtree range onto the
104    /// validator for it and calls the given function.
105    fn in_subtree<T: LayoutRaw + ?Sized, R>(
106        &mut self,
107        ptr: *const T,
108        f: impl FnOnce(&mut Self) -> Result<R, E>,
109    ) -> Result<R, E>;
110}
111
112impl<C: ArchiveContext<E> + ?Sized, E: Source> ArchiveContextExt<E> for C {
113    #[allow(clippy::not_unsafe_ptr_arg_deref)]
114    fn in_subtree_raw<R>(
115        &mut self,
116        ptr: *const u8,
117        layout: Layout,
118        f: impl FnOnce(&mut Self) -> Result<R, E>,
119    ) -> Result<R, E> {
120        self.check_subtree_ptr(ptr, &layout)?;
121
122        // SAFETY: We checked that the entire range from `ptr` to
123        // `ptr + layout.size()` is located within the buffer.
124        let range =
125            unsafe { self.push_subtree_range(ptr, ptr.add(layout.size()))? };
126
127        let result = f(self)?;
128
129        // SAFETY: `range` was returned from `push_subtree_range`.
130        unsafe {
131            self.pop_subtree_range(range)?;
132        }
133
134        Ok(result)
135    }
136
137    #[allow(clippy::not_unsafe_ptr_arg_deref)]
138    fn in_subtree<T: LayoutRaw + ?Sized, R>(
139        &mut self,
140        ptr: *const T,
141        f: impl FnOnce(&mut Self) -> Result<R, E>,
142    ) -> Result<R, E> {
143        let layout = T::layout_raw(ptr_meta::metadata(ptr)).into_error()?;
144        let root = ptr as *const u8;
145
146        self.in_subtree_raw(root, layout, f)
147    }
148}