rkyv_test/validation/mod.rs
1//! Validation implementations and helper types.
2
3pub mod owned;
4pub mod validators;
5
6use crate::{Archive, ArchivePointee, Fallible, RelPtr};
7use bytecheck::CheckBytes;
8use core::{alloc::Layout, any::TypeId, fmt};
9use ptr_meta::Pointee;
10#[cfg(feature = "std")]
11use std::error::Error;
12
13// Replace this trait with core::mem::{align_of_val_raw, size_of_val_raw} when they get stabilized.
14
15/// Gets the layout of a type from its pointer.
16pub trait LayoutRaw {
17 /// Gets the layout of the type.
18 fn layout_raw(value: *const Self) -> Layout;
19}
20
21impl<T> LayoutRaw for T {
22 #[inline]
23 fn layout_raw(_: *const Self) -> Layout {
24 Layout::new::<T>()
25 }
26}
27
28impl<T> LayoutRaw for [T] {
29 #[inline]
30 fn layout_raw(value: *const Self) -> Layout {
31 let metadata = ptr_meta::metadata(value);
32 Layout::array::<T>(metadata).unwrap()
33 }
34}
35
36impl LayoutRaw for str {
37 #[inline]
38 fn layout_raw(value: *const Self) -> Layout {
39 let metadata = ptr_meta::metadata(value);
40 Layout::array::<u8>(metadata).unwrap()
41 }
42}
43
44#[cfg(feature = "std")]
45impl LayoutRaw for ::std::ffi::CStr {
46 #[inline]
47 fn layout_raw(value: *const Self) -> Layout {
48 let metadata = ptr_meta::metadata(value);
49 Layout::array::<::std::os::raw::c_char>(metadata).unwrap()
50 }
51}
52
53/// A context that can validate nonlocal archive memory.
54pub trait ArchiveContext: Fallible {
55 /// A prefix range from an archive context.
56 ///
57 /// Ranges must be popped in the reverse order they are pushed.
58 type PrefixRange: 'static;
59
60 /// A suffix range from an archive context.
61 ///
62 /// Ranges must be popped in the reverse order they are pushed.
63 type SuffixRange: 'static;
64
65 /// Checks that a relative pointer points to an address within the archive.
66 ///
67 /// The returned pointer is not guaranteed to point to an object that is contained completely
68 /// within the archive. Use [`bounds_check_layout`](ArchiveContext::bounds_check_layout) to
69 /// verify that an object with some layout is located at the target address.
70 ///
71 /// # Safety
72 ///
73 /// - `base` must be inside the archive this valiator was created for.
74 unsafe fn bounds_check_ptr(
75 &mut self,
76 base: *const u8,
77 offset: isize,
78 ) -> Result<*const u8, Self::Error>;
79
80 /// Checks that a given pointer can be dereferenced.
81 ///
82 /// The returned pointer is guaranteed to be located within the archive. This means that the
83 /// returned pointer is safe to check, but may be vulnerable to memory overlap and recursion
84 /// attacks unless the subtree range is properly restricted. Use `check_subtree_ptr` to perform
85 /// the subtree range check as well.
86 ///
87 /// # Safety
88 ///
89 /// - `data_address` must be inside the archive this validator was created for.
90 /// - `layout` must be the layout for the given pointer.
91 unsafe fn bounds_check_layout(
92 &mut self,
93 data_address: *const u8,
94 layout: &Layout,
95 ) -> Result<(), Self::Error>;
96
97 /// Checks that the given relative pointer can be dereferenced.
98 ///
99 /// The returned pointer is guaranteed to be located within the archive. This means that the
100 /// returned pointer is safe to check, but may be vulnerable to memory overlap and recursion
101 /// attacks unless the subtree range is properly restricted. Use `check_subtree_ptr` to perform
102 /// the subtree range check as well.
103 ///
104 /// # Safety
105 ///
106 /// - `base` must be inside the archive this validator was created for.
107 /// - `metadata` must be the metadata for the pointer defined by `base` and `offset`.
108 #[inline]
109 unsafe fn check_ptr<T: LayoutRaw + Pointee + ?Sized>(
110 &mut self,
111 base: *const u8,
112 offset: isize,
113 metadata: T::Metadata,
114 ) -> Result<*const T, Self::Error> {
115 let data_address = self.bounds_check_ptr(base, offset)?;
116 let ptr = ptr_meta::from_raw_parts(data_address.cast(), metadata);
117 let layout = T::layout_raw(ptr);
118 self.bounds_check_layout(data_address, &layout)?;
119 Ok(ptr)
120 }
121
122 /// Checks that the given `RelPtr` can be dereferenced.
123 ///
124 /// The returned pointer is guaranteed to be located within the archive. This means that the
125 /// returned pointer is safe to check, but may be vulnerable to memory overlap and recursion
126 /// attacks unless the subtree range is properly restricted. Use `check_subtree_ptr` to perform
127 /// the subtree range check as well.
128 ///
129 /// # Safety
130 ///
131 /// - `rel_ptr` must be inside the archive this validator was created for.
132 #[inline]
133 unsafe fn check_rel_ptr<T: ArchivePointee + LayoutRaw + ?Sized>(
134 &mut self,
135 rel_ptr: &RelPtr<T>,
136 ) -> Result<*const T, Self::Error> {
137 let metadata = T::pointer_metadata(rel_ptr.metadata());
138 self.check_ptr(rel_ptr.base(), rel_ptr.offset(), metadata)
139 }
140
141 /// Checks that the given data address and layout is located completely within the subtree
142 /// range.
143 ///
144 /// # Safety
145 ///
146 /// - `data_address` must be inside the archive this validator was created for.
147 unsafe fn bounds_check_subtree_ptr_layout(
148 &mut self,
149 data_address: *const u8,
150 layout: &Layout,
151 ) -> Result<(), Self::Error>;
152
153 /// Checks that the given pointer is located completely within the subtree range.
154 ///
155 /// # Safety
156 ///
157 /// - `ptr` must be inside the archive this validator was created for.
158 #[inline]
159 unsafe fn bounds_check_subtree_ptr<T: LayoutRaw + ?Sized>(
160 &mut self,
161 ptr: *const T,
162 ) -> Result<(), Self::Error> {
163 let layout = T::layout_raw(ptr);
164 self.bounds_check_subtree_ptr_layout(ptr.cast(), &layout)
165 }
166
167 /// Checks that the given relative pointer to a subtree can be dereferenced.
168 ///
169 /// # Safety
170 ///
171 /// - `base` must be inside the archive this validator was created for.
172 /// - `metadata` must be the metadata for the pointer defined by `base` and `offset`.
173 #[inline]
174 unsafe fn check_subtree_ptr<T: LayoutRaw + Pointee + ?Sized>(
175 &mut self,
176 base: *const u8,
177 offset: isize,
178 metadata: T::Metadata,
179 ) -> Result<*const T, Self::Error> {
180 let ptr = self.check_ptr(base, offset, metadata)?;
181 self.bounds_check_subtree_ptr(ptr)?;
182 Ok(ptr)
183 }
184
185 /// Checks that the given `RelPtr` to a subtree can be dereferenced.
186 ///
187 /// # Safety
188 ///
189 /// - `rel_ptr` must be inside the archive this validator was created for.
190 #[inline]
191 unsafe fn check_subtree_rel_ptr<T: ArchivePointee + LayoutRaw + ?Sized>(
192 &mut self,
193 rel_ptr: &RelPtr<T>,
194 ) -> Result<*const T, Self::Error> {
195 let ptr = self.check_rel_ptr(rel_ptr)?;
196 self.bounds_check_subtree_ptr(ptr)?;
197 Ok(ptr)
198 }
199
200 /// Pushes a new subtree range onto the validator and starts validating it.
201 ///
202 /// After calling `push_subtree_claim_to`, the validator will have a subtree range starting at
203 /// the original start and ending at `root`. After popping the returned range, the validator
204 /// will have a subtree range starting at `end` and ending at the original end.
205 ///
206 /// # Safety
207 ///
208 /// `root` and `end` must be located inside the archive.
209 unsafe fn push_prefix_subtree_range(
210 &mut self,
211 root: *const u8,
212 end: *const u8,
213 ) -> Result<Self::PrefixRange, Self::Error>;
214
215 /// Pushes a new subtree range onto the validator and starts validating it.
216 ///
217 /// The claimed range spans from the end of `start` to the end of the current subobject range.
218 ///
219 /// # Safety
220 ///
221 /// `` must be located inside the archive.
222 #[inline]
223 unsafe fn push_prefix_subtree<T: LayoutRaw + ?Sized>(
224 &mut self,
225 root: *const T,
226 ) -> Result<Self::PrefixRange, Self::Error> {
227 let layout = T::layout_raw(root);
228 self.push_prefix_subtree_range(root as *const u8, (root as *const u8).add(layout.size()))
229 }
230
231 /// Pops the given range, restoring the original state with the pushed range removed.
232 ///
233 /// If the range was not popped in reverse order, an error is returned.
234 fn pop_prefix_range(&mut self, range: Self::PrefixRange) -> Result<(), Self::Error>;
235
236 /// Pushes a new subtree range onto the validator and starts validating it.
237 ///
238 /// After calling `push_prefix_subtree_range`, the validator will have a subtree range starting
239 /// at `start` and ending at `root`. After popping the returned range, the validator will have a
240 /// subtree range starting at the original start and ending at `start`.
241 ///
242 /// # Safety
243 ///
244 /// `start` and `root` must be located inside the archive.
245 unsafe fn push_suffix_subtree_range(
246 &mut self,
247 start: *const u8,
248 root: *const u8,
249 ) -> Result<Self::SuffixRange, Self::Error>;
250
251 /// Finishes the given range, restoring the original state with the pushed range removed.
252 ///
253 /// If the range was not popped in reverse order, an error is returned.
254 fn pop_suffix_range(&mut self, range: Self::SuffixRange) -> Result<(), Self::Error>;
255
256 /// Verifies that all outstanding claims have been returned.
257 fn finish(&mut self) -> Result<(), Self::Error>;
258}
259
260/// A context that can validate shared archive memory.
261///
262/// Shared pointers require this kind of context to validate.
263pub trait SharedContext: Fallible {
264 /// Registers the given `ptr` as a shared pointer with the given type.
265 ///
266 /// Returns `true` if the pointer was newly-registered and `check_bytes` should be called.
267 fn register_shared_ptr(&mut self, ptr: *const u8, type_id: TypeId)
268 -> Result<bool, Self::Error>;
269}
270
271/// Errors that can occur when checking an archive.
272#[derive(Debug)]
273pub enum CheckArchiveError<T, C> {
274 /// An error that occurred while validating an object
275 CheckBytesError(T),
276 /// A context error occurred
277 ContextError(C),
278}
279
280impl<T: fmt::Display, C: fmt::Display> fmt::Display for CheckArchiveError<T, C> {
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 match self {
283 CheckArchiveError::CheckBytesError(e) => write!(f, "check bytes error: {}", e),
284 CheckArchiveError::ContextError(e) => write!(f, "context error: {}", e),
285 }
286 }
287}
288
289#[cfg(feature = "std")]
290impl<T: Error + 'static, C: Error + 'static> Error for CheckArchiveError<T, C> {
291 fn source(&self) -> Option<&(dyn Error + 'static)> {
292 match self {
293 CheckArchiveError::CheckBytesError(e) => Some(e as &dyn Error),
294 CheckArchiveError::ContextError(e) => Some(e as &dyn Error),
295 }
296 }
297}
298
299/// The error type that can be produced by checking the given type with the given validator.
300pub type CheckTypeError<T, C> =
301 CheckArchiveError<<T as CheckBytes<C>>::Error, <C as Fallible>::Error>;
302
303// TODO: change this to be the public-facing API (uses pos: isize instead of pos: usize)
304#[inline]
305fn internal_check_archived_value_with_context<'a, T, C>(
306 buf: &'a [u8],
307 pos: isize,
308 context: &mut C,
309) -> Result<&'a T::Archived, CheckTypeError<T::Archived, C>>
310where
311 T: Archive,
312 T::Archived: CheckBytes<C> + Pointee<Metadata = ()>,
313 C: ArchiveContext + ?Sized,
314{
315 unsafe {
316 let ptr = context
317 .check_subtree_ptr(buf.as_ptr(), pos, ())
318 .map_err(CheckArchiveError::ContextError)?;
319
320 let range = context
321 .push_prefix_subtree(ptr)
322 .map_err(CheckArchiveError::ContextError)?;
323 let result =
324 CheckBytes::check_bytes(ptr, context).map_err(CheckArchiveError::CheckBytesError)?;
325 context
326 .pop_prefix_range(range)
327 .map_err(CheckArchiveError::ContextError)?;
328
329 context.finish().map_err(CheckArchiveError::ContextError)?;
330 Ok(result)
331 }
332}
333
334/// Checks the given archive with an additional context.
335///
336/// See [`check_archived_value`](crate::validation::validators::check_archived_value) for more details.
337#[inline]
338pub fn check_archived_value_with_context<'a, T, C>(
339 buf: &'a [u8],
340 pos: usize,
341 context: &mut C,
342) -> Result<&'a T::Archived, CheckTypeError<T::Archived, C>>
343where
344 T: Archive,
345 T::Archived: CheckBytes<C> + Pointee<Metadata = ()>,
346 C: ArchiveContext + ?Sized,
347{
348 internal_check_archived_value_with_context::<T, C>(buf, pos as isize, context)
349}
350
351/// Checks the given archive with an additional context.
352///
353/// See [`check_archived_value`](crate::validation::validators::check_archived_value) for more details.
354#[inline]
355pub fn check_archived_root_with_context<'a, T, C>(
356 buf: &'a [u8],
357 context: &mut C,
358) -> Result<&'a T::Archived, CheckTypeError<T::Archived, C>>
359where
360 T: Archive,
361 T::Archived: CheckBytes<C> + Pointee<Metadata = ()>,
362 C: ArchiveContext + ?Sized,
363{
364 internal_check_archived_value_with_context::<T, C>(
365 buf,
366 buf.len() as isize - core::mem::size_of::<T::Archived>() as isize,
367 context,
368 )
369}