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}