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}