rkyv/validation/
mod.rs

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