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}