Skip to main content

rkyv/validation/archive/
validator.rs

1use core::{
2    alloc::Layout, error::Error, fmt, marker::PhantomData, num::NonZeroUsize,
3    ops::Range,
4};
5
6use rancor::{fail, OptionExt, Source};
7
8use crate::validation::ArchiveContext;
9
10const PTR_WIDTH: usize = (usize::BITS / 4 + 2) as usize;
11
12struct Pointer(pub usize);
13
14impl fmt::Display for Pointer {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        write!(f, "{:#0w$x}", self.0, w = PTR_WIDTH)
17    }
18}
19
20#[derive(Debug)]
21struct UnalignedPointer {
22    address: usize,
23    align: usize,
24}
25
26impl fmt::Display for UnalignedPointer {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        write!(
29            f,
30            "unaligned pointer: ptr {} unaligned for alignment {}",
31            Pointer(self.address),
32            self.align,
33        )
34    }
35}
36
37impl Error for UnalignedPointer {}
38
39#[derive(Debug)]
40struct InvalidSubtreePointer {
41    address: usize,
42    size: usize,
43    subtree_range: Range<usize>,
44}
45
46impl fmt::Display for InvalidSubtreePointer {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(
49            f,
50            "subtree pointer overran range: ptr {} size {} in range {}..{}",
51            Pointer(self.address),
52            self.size,
53            Pointer(self.subtree_range.start),
54            Pointer(self.subtree_range.end),
55        )
56    }
57}
58
59impl Error for InvalidSubtreePointer {}
60
61#[derive(Debug)]
62struct ExceededMaximumSubtreeDepth;
63
64impl fmt::Display for ExceededMaximumSubtreeDepth {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        write!(
67            f,
68            "pushed a subtree range that exceeded the maximum subtree depth",
69        )
70    }
71}
72
73impl Error for ExceededMaximumSubtreeDepth {}
74
75#[derive(Debug)]
76struct RangePoppedTooManyTimes;
77
78impl fmt::Display for RangePoppedTooManyTimes {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        write!(f, "subtree range popped too many times")
81    }
82}
83
84impl Error for RangePoppedTooManyTimes {}
85
86#[derive(Debug)]
87struct RangePoppedOutOfOrder;
88
89impl fmt::Display for RangePoppedOutOfOrder {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        write!(f, "subtree range popped out of order")
92    }
93}
94
95impl Error for RangePoppedOutOfOrder {}
96
97/// A validator that can verify archives with nonlocal memory.
98#[derive(Debug)]
99pub struct ArchiveValidator<'a> {
100    subtree_range: Range<usize>,
101    max_subtree_depth: Option<NonZeroUsize>,
102    _phantom: PhantomData<&'a [u8]>,
103}
104
105impl<'a> ArchiveValidator<'a> {
106    /// Creates a new bounds validator for the given bytes.
107    #[inline]
108    pub fn new(bytes: &'a [u8]) -> Self {
109        Self::with_max_depth(bytes, None)
110    }
111
112    /// Crates a new bounds validator for the given bytes with a maximum
113    /// validation depth.
114    #[inline]
115    pub fn with_max_depth(
116        bytes: &'a [u8],
117        max_subtree_depth: Option<NonZeroUsize>,
118    ) -> Self {
119        let Range { start, end } = bytes.as_ptr_range();
120        Self {
121            subtree_range: Range {
122                start: start as usize,
123                end: end as usize,
124            },
125            max_subtree_depth,
126            _phantom: PhantomData,
127        }
128    }
129}
130
131unsafe impl<E: Source> ArchiveContext<E> for ArchiveValidator<'_> {
132    fn check_subtree_ptr(
133        &mut self,
134        ptr: *const u8,
135        layout: &Layout,
136    ) -> Result<(), E> {
137        let start = ptr as usize;
138        let end = ptr.wrapping_add(layout.size()) as usize;
139        if end < start
140            || start < self.subtree_range.start
141            || end > self.subtree_range.end
142        {
143            fail!(InvalidSubtreePointer {
144                address: start,
145                size: layout.size(),
146                subtree_range: self.subtree_range.clone(),
147            });
148        } else if start & (layout.align() - 1) != 0 {
149            fail!(UnalignedPointer {
150                address: ptr as usize,
151                align: layout.align(),
152            });
153        } else {
154            Ok(())
155        }
156    }
157
158    unsafe fn push_subtree_range(
159        &mut self,
160        root: *const u8,
161        end: *const u8,
162    ) -> Result<Range<usize>, E> {
163        if let Some(max_subtree_depth) = &mut self.max_subtree_depth {
164            *max_subtree_depth = NonZeroUsize::new(max_subtree_depth.get() - 1)
165                .into_trace(ExceededMaximumSubtreeDepth)?;
166        }
167
168        let result = Range {
169            start: end as usize,
170            end: self.subtree_range.end,
171        };
172        self.subtree_range.end = root as usize;
173        Ok(result)
174    }
175
176    unsafe fn pop_subtree_range(
177        &mut self,
178        range: Range<usize>,
179    ) -> Result<(), E> {
180        if range.start < self.subtree_range.end {
181            fail!(RangePoppedOutOfOrder);
182        }
183        self.subtree_range = range;
184        if let Some(max_subtree_depth) = &mut self.max_subtree_depth {
185            *max_subtree_depth = max_subtree_depth
186                .checked_add(1)
187                .into_trace(RangePoppedTooManyTimes)?;
188        }
189        Ok(())
190    }
191}