rkyv_dyn/
validation.rs

1//! Validation implementations and helper types.
2
3use crate::{ArchivedDynMetadata, RegisteredImpl, IMPL_REGISTRY};
4use bytecheck::CheckBytes;
5#[cfg(feature = "vtable_cache")]
6use core::sync::atomic::Ordering;
7use core::{
8    alloc::Layout,
9    any::{Any, TypeId},
10    convert::Infallible,
11    fmt,
12    marker::PhantomData,
13    ptr,
14};
15use rkyv::{
16    from_archived,
17    validation::{ArchiveContext, SharedContext},
18    Archived, Fallible,
19};
20use rkyv_typename::TypeName;
21use std::{collections::HashMap, error::Error};
22
23/// A context that's object safe and suitable for checking most types.
24pub trait DynContext {
25    /// Checks that a relative pointer points to an address within the archive.
26    ///
27    /// See [`bounds_check_ptr`] for more information.
28    ///
29    /// # Safety
30    ///
31    /// - `base` must be inside the archive this validator was created for.
32    ///
33    /// [`bounds_check_ptr`]: rkyv::validation::ArchiveContext::bounds_check_ptr
34    unsafe fn bounds_check_ptr_dyn(
35        &mut self,
36        base: *const u8,
37        offset: isize,
38    ) -> Result<*const u8, Box<dyn Error>>;
39
40    /// Checks that a given pointer can be dereferenced.
41    ///
42    /// See [`bounds_check_layout`] for more information.
43    ///
44    /// # Safety
45    ///
46    /// - `data_address` must be inside the archive this validator was created for.
47    /// - `layout` must be the layout for the given pointer.
48    ///
49    /// [`bounds_check_layout`]: rkyv::validation::ArchiveContext::bounds_check_layout
50    unsafe fn bounds_check_layout_dyn(
51        &mut self,
52        data_address: *const u8,
53        layout: &Layout,
54    ) -> Result<(), Box<dyn Error>>;
55
56    /// Checks that the given data address and layout is located completely within the subtree
57    /// range.
58    ///
59    /// See [`bounds_check_subtree_ptr_layout`] for more information.
60    ///
61    /// # Safety
62    ///
63    /// - `data_address` must be inside the archive this validator was created for.
64    ///
65    /// [`bounds_check_subtree_ptr_layout`]: rkyv::validation::ArchiveContext::bounds_check_subtree_ptr_layout
66    unsafe fn bounds_check_subtree_ptr_layout_dyn(
67        &mut self,
68        data_address: *const u8,
69        layout: &Layout,
70    ) -> Result<(), Box<dyn Error>>;
71
72    /// Pushes a new subtree range onto the validator and starts validating it.
73    ///
74    /// See [`push_prefix_subtree_range`] for more information.
75    ///
76    /// # Safety
77    ///
78    /// `root` and `end` must be located inside the archive.
79    ///
80    /// [`push_prefix_subtree_range`]: rkyv::validation::ArchiveContext::push_prefix_subtree_range
81    unsafe fn push_prefix_subtree_range_dyn(
82        &mut self,
83        root: *const u8,
84        end: *const u8,
85    ) -> Result<Box<dyn Any>, Box<dyn Error>>;
86
87    /// Pops the given range, restoring the original state with the pushed range removed.
88    ///
89    /// See [`pop_prefix_range`] for more information.
90    ///
91    /// [`pop_prefix_range`]: rkyv::validation::ArchiveContext::pop_prefix_range
92    fn pop_prefix_range_dyn(&mut self, range: Box<dyn Any>) -> Result<(), Box<dyn Error>>;
93
94    /// Pushes a new subtree range onto the validator and starts validating it.
95    ///
96    /// See [`push_suffix_subtree_range`] for more information.
97    ///
98    /// # Safety
99    ///
100    /// `start` and `root` must be located inside the archive.
101    ///
102    /// [`push_suffix_subtree_range`]: rkyv::validation::ArchiveContext::push_suffix_subtree_range
103    unsafe fn push_suffix_subtree_range_dyn(
104        &mut self,
105        start: *const u8,
106        root: *const u8,
107    ) -> Result<Box<dyn Any>, Box<dyn Error>>;
108
109    /// Finishes the given range, restoring the original state with the pushed range removed.
110    ///
111    /// See [`pop_suffix_range`] for more information.
112    ///
113    /// [`pop_suffix_range`]: rkyv::validation::ArchiveContext::pop_suffix_range
114    fn pop_suffix_range_dyn(&mut self, range: Box<dyn Any>) -> Result<(), Box<dyn Error>>;
115
116    /// Verifies that all outstanding claims have been returned.
117    ///
118    /// See [`finish`] for more information.
119    ///
120    /// [`finish`]: rkyv::validation::ArchiveContext::finish
121    fn finish_dyn(&mut self) -> Result<(), Box<dyn Error>>;
122
123    /// Registers the given `ptr` as a shared pointer with the given type.
124    ///
125    /// See [`register_shared_ptr`] for more information.
126    ///
127    /// [`register_shared_ptr`]: rkyv::validation::SharedContext::register_shared_ptr
128    fn register_shared_ptr_dyn(
129        &mut self,
130        ptr: *const u8,
131        type_id: TypeId,
132    ) -> Result<bool, Box<dyn Error>>;
133}
134
135impl<C> DynContext for C
136where
137    C: ArchiveContext + SharedContext + ?Sized,
138    C::Error: Error,
139{
140    unsafe fn bounds_check_ptr_dyn(
141        &mut self,
142        base: *const u8,
143        offset: isize,
144    ) -> Result<*const u8, Box<dyn Error>> {
145        self.bounds_check_ptr(base, offset)
146            .map_err(|e| Box::new(e) as Box<dyn Error>)
147    }
148
149    unsafe fn bounds_check_layout_dyn(
150        &mut self,
151        ptr: *const u8,
152        layout: &Layout,
153    ) -> Result<(), Box<dyn Error>> {
154        self.bounds_check_layout(ptr, layout)
155            .map_err(|e| Box::new(e) as Box<dyn Error>)
156    }
157
158    unsafe fn bounds_check_subtree_ptr_layout_dyn(
159        &mut self,
160        data_address: *const u8,
161        layout: &Layout,
162    ) -> Result<(), Box<dyn Error>> {
163        self.bounds_check_subtree_ptr_layout(data_address, layout)
164            .map_err(|e| Box::new(e) as Box<dyn Error>)
165    }
166
167    unsafe fn push_prefix_subtree_range_dyn(
168        &mut self,
169        root: *const u8,
170        end: *const u8,
171    ) -> Result<Box<dyn Any>, Box<dyn Error>> {
172        self.push_prefix_subtree_range(root, end)
173            .map(|r| Box::new(r) as Box<dyn Any>)
174            .map_err(|e| Box::new(e) as Box<dyn Error>)
175    }
176
177    fn pop_prefix_range_dyn(&mut self, range: Box<dyn Any>) -> Result<(), Box<dyn Error>> {
178        self.pop_prefix_range(*range.downcast().unwrap())
179            .map_err(|e| Box::new(e) as Box<dyn Error>)
180    }
181
182    unsafe fn push_suffix_subtree_range_dyn(
183        &mut self,
184        start: *const u8,
185        root: *const u8,
186    ) -> Result<Box<dyn Any>, Box<dyn Error>> {
187        self.push_suffix_subtree_range(start, root)
188            .map(|r| Box::new(r) as Box<dyn Any>)
189            .map_err(|e| Box::new(e) as Box<dyn Error>)
190    }
191
192    fn pop_suffix_range_dyn(&mut self, range: Box<dyn Any>) -> Result<(), Box<dyn Error>> {
193        self.pop_suffix_range(*range.downcast().unwrap())
194            .map_err(|e| Box::new(e) as Box<dyn Error>)
195    }
196
197    fn finish_dyn(&mut self) -> Result<(), Box<dyn Error>> {
198        self.finish().map_err(|e| Box::new(e) as Box<dyn Error>)
199    }
200
201    fn register_shared_ptr_dyn(
202        &mut self,
203        ptr: *const u8,
204        type_id: TypeId,
205    ) -> Result<bool, Box<dyn Error>> {
206        self.register_shared_ptr(ptr, type_id)
207            .map_err(|e| Box::new(e) as Box<dyn Error>)
208    }
209}
210
211/// The error type for `DynContext`.
212pub struct DynError {
213    inner: Box<dyn Error>,
214}
215
216impl From<Box<dyn Error>> for DynError {
217    fn from(inner: Box<dyn Error>) -> Self {
218        Self { inner }
219    }
220}
221
222impl fmt::Display for DynError {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        self.inner.fmt(f)
225    }
226}
227
228impl fmt::Debug for DynError {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        self.inner.fmt(f)
231    }
232}
233
234impl Error for DynError {}
235
236impl Fallible for (dyn DynContext + '_) {
237    type Error = DynError;
238}
239
240impl ArchiveContext for (dyn DynContext + '_) {
241    type PrefixRange = Box<dyn Any>;
242    type SuffixRange = Box<dyn Any>;
243
244    unsafe fn bounds_check_ptr(
245        &mut self,
246        base: *const u8,
247        offset: isize,
248    ) -> Result<*const u8, Self::Error> {
249        Ok(self.bounds_check_ptr_dyn(base, offset)?)
250    }
251
252    unsafe fn bounds_check_layout(
253        &mut self,
254        data_address: *const u8,
255        layout: &Layout,
256    ) -> Result<(), Self::Error> {
257        Ok(self.bounds_check_layout_dyn(data_address, layout)?)
258    }
259
260    unsafe fn bounds_check_subtree_ptr_layout(
261        &mut self,
262        data_address: *const u8,
263        layout: &Layout,
264    ) -> Result<(), Self::Error> {
265        Ok(self.bounds_check_subtree_ptr_layout_dyn(data_address, layout)?)
266    }
267
268    unsafe fn push_prefix_subtree_range(
269        &mut self,
270        root: *const u8,
271        end: *const u8,
272    ) -> Result<Self::PrefixRange, Self::Error> {
273        Ok(self.push_prefix_subtree_range_dyn(root, end)?)
274    }
275
276    fn pop_prefix_range(&mut self, range: Self::PrefixRange) -> Result<(), Self::Error> {
277        Ok(self.pop_prefix_range_dyn(range)?)
278    }
279
280    unsafe fn push_suffix_subtree_range(
281        &mut self,
282        start: *const u8,
283        root: *const u8,
284    ) -> Result<Self::SuffixRange, Self::Error> {
285        Ok(self.push_suffix_subtree_range_dyn(start, root)?)
286    }
287
288    fn pop_suffix_range(&mut self, range: Self::SuffixRange) -> Result<(), Self::Error> {
289        Ok(self.pop_suffix_range_dyn(range)?)
290    }
291
292    fn wrap_layout_error(layout_error: core::alloc::LayoutError) -> Self::Error {
293        DynError {
294            inner: Box::new(layout_error) as Box<dyn Error>,
295        }
296    }
297    fn finish(&mut self) -> Result<(), Self::Error> {
298        Ok(self.finish_dyn()?)
299    }
300}
301
302impl SharedContext for (dyn DynContext + '_) {
303    fn register_shared_ptr(&mut self, ptr: *const u8, type_id: TypeId) -> Result<bool, DynError> {
304        Ok(self.register_shared_ptr_dyn(ptr, type_id)?)
305    }
306}
307
308// This error just always says that check bytes isn't implemented for a type
309#[derive(Debug)]
310struct CheckBytesUnimplemented;
311
312impl fmt::Display for CheckBytesUnimplemented {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        write!(f, "check bytes is not implemented for this type")
315    }
316}
317
318impl Error for CheckBytesUnimplemented {}
319
320type CheckBytesDyn = unsafe fn(*const u8, &mut dyn DynContext) -> Result<(), Box<dyn Error>>;
321
322// This is the fallback function that gets called if the archived type doesn't implement CheckBytes.
323#[inline]
324unsafe fn check_bytes_dyn_unimplemented(
325    _bytes: *const u8,
326    _context: &mut dyn DynContext,
327) -> Result<(), Box<dyn Error>> {
328    Err(Box::new(CheckBytesUnimplemented).into())
329}
330
331#[doc(hidden)]
332pub trait NotCheckBytesDyn {
333    const CHECK_BYTES_DYN: CheckBytesDyn = check_bytes_dyn_unimplemented;
334}
335
336impl<T: ?Sized> NotCheckBytesDyn for T {}
337
338#[doc(hidden)]
339pub struct IsCheckBytesDyn<T: ?Sized>(PhantomData<T>);
340
341impl<T: for<'a> CheckBytes<dyn DynContext + 'a>> IsCheckBytesDyn<T> {
342    #[doc(hidden)]
343    pub const CHECK_BYTES_DYN: CheckBytesDyn = Self::check_bytes_dyn;
344
345    #[inline]
346    unsafe fn check_bytes_dyn(
347        bytes: *const u8,
348        context: &mut dyn DynContext,
349    ) -> Result<(), Box<dyn Error>> {
350        T::check_bytes(bytes.cast(), context)?;
351        Ok(())
352    }
353}
354
355#[doc(hidden)]
356#[derive(Copy, Clone)]
357pub struct ImplValidation {
358    pub layout: Layout,
359    pub check_bytes_dyn: CheckBytesDyn,
360}
361
362#[doc(hidden)]
363#[macro_export]
364macro_rules! validation {
365    ($type:ty as $trait:ty) => {
366        use rkyv_dyn::validation::{ImplValidation, IsCheckBytesDyn, NotCheckBytesDyn};
367    };
368}
369
370/// Errors that can occur when checking archived trait objects
371#[derive(Debug)]
372pub enum DynMetadataError {
373    /// The trait object has an invalid type id
374    InvalidImplId(u64),
375    /// The cached vtable does not match the vtable for the type id
376    MismatchedCachedVtable {
377        /// The type id of the trait object
378        type_id: u64,
379        /// The expected vtable
380        expected: usize,
381        /// The found vtable
382        found: usize,
383    },
384}
385
386impl fmt::Display for DynMetadataError {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        match self {
389            DynMetadataError::InvalidImplId(id) => {
390                write!(f, "invalid impl id: {} not registered", id)
391            }
392            DynMetadataError::MismatchedCachedVtable {
393                type_id,
394                expected,
395                found,
396            } => {
397                write!(
398                    f,
399                    "mismatched cached vtable for {}: expected {} but found {}",
400                    type_id, expected, found
401                )
402            }
403        }
404    }
405}
406
407impl Error for DynMetadataError {}
408
409impl From<Infallible> for DynMetadataError {
410    fn from(_: Infallible) -> Self {
411        unsafe { core::hint::unreachable_unchecked() }
412    }
413}
414
415impl<T: TypeName + ?Sized, C: ?Sized> CheckBytes<C> for ArchivedDynMetadata<T> {
416    type Error = DynMetadataError;
417
418    unsafe fn check_bytes<'a>(
419        value: *const Self,
420        context: &mut C,
421    ) -> Result<&'a Self, Self::Error> {
422        let type_id = from_archived!(*Archived::<u64>::check_bytes(
423            ptr::addr_of!((*value).type_id),
424            context,
425        )?);
426        PhantomData::<T>::check_bytes(ptr::addr_of!((*value).phantom), context)?;
427        if let Some(impl_data) = IMPL_REGISTRY.get::<T>(type_id) {
428            let cached_vtable_ptr = ptr::addr_of!((*value).cached_vtable);
429            #[cfg(feature = "vtable_cache")]
430            let cached_vtable =
431                CheckBytes::check_bytes(cached_vtable_ptr, context)?.load(Ordering::Relaxed);
432            #[cfg(not(feature = "vtable_cache"))]
433            let cached_vtable =
434                from_archived!(*Archived::<u64>::check_bytes(cached_vtable_ptr, context)?);
435            if cached_vtable == 0 || cached_vtable as usize == impl_data.vtable {
436                Ok(&*value)
437            } else {
438                Err(DynMetadataError::MismatchedCachedVtable {
439                    type_id,
440                    expected: impl_data.vtable,
441                    found: cached_vtable as usize,
442                })
443            }
444        } else {
445            Err(DynMetadataError::InvalidImplId(type_id))
446        }
447    }
448}
449
450/// Errors that can occur when checking archived trait objects
451#[derive(Debug)]
452pub enum CheckDynError {
453    /// The pointer metadata did not match any registered impl
454    InvalidMetadata(u64),
455    /// An error occurred while checking the bytes of the trait object
456    CheckBytes(Box<dyn Error>),
457}
458
459impl fmt::Display for CheckDynError {
460    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461        match self {
462            CheckDynError::InvalidMetadata(n) => write!(f, "invalid metadata: {}", n),
463            CheckDynError::CheckBytes(e) => write!(f, "check bytes: {}", e),
464        }
465    }
466}
467
468impl Error for CheckDynError {
469    fn source(&self) -> Option<&(dyn Error + 'static)> {
470        match self {
471            CheckDynError::InvalidMetadata(_) => None,
472            CheckDynError::CheckBytes(e) => Some(e.as_ref()),
473        }
474    }
475}
476
477impl From<Box<dyn Error>> for CheckDynError {
478    fn from(e: Box<dyn Error>) -> Self {
479        Self::CheckBytes(e)
480    }
481}
482
483#[doc(hidden)]
484pub struct CheckBytesEntry {
485    vtable: usize,
486    validation: ImplValidation,
487}
488
489impl CheckBytesEntry {
490    #[doc(hidden)]
491    pub fn new<TY: RegisteredImpl<TR>, TR: ?Sized>(check_bytes_dyn: CheckBytesDyn) -> Self {
492        Self {
493            vtable: <TY as RegisteredImpl<TR>>::vtable(),
494            validation: ImplValidation {
495                layout: Layout::new::<TY>(),
496                check_bytes_dyn,
497            },
498        }
499    }
500}
501
502inventory::collect!(CheckBytesEntry);
503
504#[doc(hidden)]
505pub struct CheckBytesRegistry {
506    vtable_to_check_bytes: HashMap<usize, ImplValidation>,
507}
508
509impl CheckBytesRegistry {
510    fn new() -> Self {
511        Self {
512            vtable_to_check_bytes: HashMap::new(),
513        }
514    }
515
516    fn add_entry(&mut self, entry: &CheckBytesEntry) {
517        let old_value = self
518            .vtable_to_check_bytes
519            .insert(entry.vtable, entry.validation);
520
521        debug_assert!(old_value.is_none(), "vtable conflict, a trait implementation was likely added twice (but it's possible there was a hash collision)");
522    }
523
524    #[doc(hidden)]
525    pub fn get(&self, vtable: usize) -> Option<&ImplValidation> {
526        self.vtable_to_check_bytes.get(&vtable)
527    }
528}
529
530lazy_static::lazy_static! {
531    #[doc(hidden)]
532    pub static ref CHECK_BYTES_REGISTRY: CheckBytesRegistry =  {
533        let mut result = CheckBytesRegistry::new();
534        for entry in inventory::iter::<CheckBytesEntry> {
535            result.add_entry(entry);
536        }
537        result
538    };
539}
540
541#[doc(hidden)]
542#[macro_export]
543macro_rules! register_validation {
544    ($type:ty as $trait:ty) => {
545        use rkyv_dyn::validation::{CheckBytesEntry, IsCheckBytesDyn, NotCheckBytesDyn};
546
547        inventory::submit! { CheckBytesEntry::new::<$type, $trait>(IsCheckBytesDyn::<$type>::CHECK_BYTES_DYN) }
548    }
549}