rkyv_test/validation/validators/
archive.rs

1//! The provided implementation for `ArchiveContext`.
2
3use crate::{validation::ArchiveContext, Fallible};
4use core::{alloc::Layout, fmt, ops::Range};
5
6/// Errors that can occur when checking archive memory.
7#[derive(Debug)]
8pub enum ArchiveError {
9    /// Computing the target of a relative pointer overflowed
10    Overflow {
11        /// The base pointer
12        base: *const u8,
13        /// The offset
14        offset: isize,
15    },
16    /// The archive is under-aligned for one of the types inside
17    Underaligned {
18        /// The expected alignment of the archive
19        expected_align: usize,
20        /// The actual alignment of the archive
21        actual_align: usize,
22    },
23    /// A pointer pointed outside the bounds of the archive
24    OutOfBounds {
25        /// The base of the relative pointer
26        base: *const u8,
27        /// The offset of the relative pointer
28        offset: isize,
29        /// The pointer range of the archive
30        range: Range<*const u8>,
31    },
32    /// There wasn't enough space for the desired type at the pointed location
33    Overrun {
34        /// The pointer to the type
35        ptr: *const u8,
36        /// The desired size of the type
37        size: usize,
38        /// The pointer range of the archive
39        range: Range<*const u8>,
40    },
41    /// The pointer wasn't aligned properly for the desired type
42    Unaligned {
43        /// The pointer to the type
44        ptr: *const u8,
45        /// The required alignment of the type
46        align: usize,
47    },
48    /// The pointer wasn't within the subtree range
49    SubtreePointerOutOfBounds {
50        /// The pointer to the subtree
51        ptr: *const u8,
52        /// The subtree range
53        subtree_range: Range<*const u8>,
54    },
55    /// There wasn't enough space in the subtree range for the desired type at the pointed location
56    SubtreePointerOverrun {
57        /// The pointer to the subtree type,
58        ptr: *const u8,
59        /// The desired size of the type
60        size: usize,
61        /// The subtree range
62        subtree_range: Range<*const u8>,
63    },
64    /// A subtree range was popped out of order.
65    ///
66    /// Subtree ranges must be popped in the reverse of the order they are pushed.
67    RangePoppedOutOfOrder {
68        /// The expected depth of the range
69        expected_depth: usize,
70        /// The actual depth of the range
71        actual_depth: usize,
72    },
73    /// A subtree range was not popped before validation concluded.
74    UnpoppedSubtreeRanges {
75        /// The depth of the last subtree that was pushed
76        last_range: usize,
77    },
78    /// The maximum subtree depth was reached or exceeded.
79    ExceededMaximumSubtreeDepth {
80        /// The maximum depth that subtrees may be validated down to
81        max_subtree_depth: usize,
82    },
83}
84
85// SAFETY: ArchiveError is safe to send to another thread
86// This trait is not automatically implemented because the enum contains a pointer
87unsafe impl Send for ArchiveError {}
88
89// SAFETY: ArchiveError is safe to share between threads
90// This trait is not automatically implemented because the enum contains a pointer
91unsafe impl Sync for ArchiveError {}
92
93impl fmt::Display for ArchiveError {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        match self {
96            ArchiveError::Overflow { base, offset } => write!(
97                f,
98                "relative pointer overflowed: base {:p} offset {}",
99                base, offset
100            ),
101            ArchiveError::Underaligned {
102                expected_align,
103                actual_align,
104            } => write!(
105                f,
106                "archive underaligned: need alignment {} but have alignment {}",
107                expected_align, actual_align
108            ),
109            ArchiveError::OutOfBounds {
110                base,
111                offset,
112                range,
113            } => write!(
114                f,
115                "pointer out of bounds: base {:p} offset {} not in range {:p}..{:p}",
116                base, offset, range.start, range.end
117            ),
118            ArchiveError::Overrun { ptr, size, range } => write!(
119                f,
120                "pointer overran buffer: ptr {:p} size {} in range {:p}..{:p}",
121                ptr, size, range.start, range.end
122            ),
123            ArchiveError::Unaligned { ptr, align } => write!(
124                f,
125                "unaligned pointer: ptr {:p} unaligned for alignment {}",
126                ptr, align
127            ),
128            ArchiveError::SubtreePointerOutOfBounds { ptr, subtree_range } => write!(
129                f,
130                "subtree pointer out of bounds: ptr {:p} not in range {:p}..{:p}",
131                ptr, subtree_range.start, subtree_range.end
132            ),
133            ArchiveError::SubtreePointerOverrun {
134                ptr,
135                size,
136                subtree_range,
137            } => write!(
138                f,
139                "subtree pointer overran range: ptr {:p} size {} in range {:p}..{:p}",
140                ptr, size, subtree_range.start, subtree_range.end
141            ),
142            ArchiveError::RangePoppedOutOfOrder {
143                expected_depth,
144                actual_depth,
145            } => write!(
146                f,
147                "subtree range popped out of order: expected depth {}, actual depth {}",
148                expected_depth, actual_depth
149            ),
150            ArchiveError::UnpoppedSubtreeRanges { last_range } => {
151                write!(f, "unpopped subtree ranges: last range {}", last_range)
152            }
153            ArchiveError::ExceededMaximumSubtreeDepth { max_subtree_depth } => write!(
154                f,
155                "pushed a subtree range that exceeded the maximum subtree depth of {}",
156                max_subtree_depth
157            ),
158        }
159    }
160}
161
162#[cfg(feature = "std")]
163impl std::error::Error for ArchiveError {}
164
165/// A prefix range from an [`ArchiveValidator`].
166#[derive(Debug)]
167pub struct PrefixRange {
168    range: Range<*const u8>,
169    depth: usize,
170}
171
172// SAFETY: PrefixRange is safe to send to another thread
173// This trait is not automatically implemented because the struct contains a pointer
174unsafe impl<'a> Send for PrefixRange {}
175
176// SAFETY: PrefixRange is safe to share between threads
177// This trait is not automatically implemented because the struct contains a pointer
178unsafe impl Sync for PrefixRange {}
179
180/// A suffix range from an [`ArchiveValidator`].
181#[derive(Debug)]
182pub struct SuffixRange {
183    start: *const u8,
184    depth: usize,
185}
186
187// SAFETY: SuffixRange is safe to send to another thread
188// This trait is not automatically implemented because the struct contains a pointer
189unsafe impl<'a> Send for SuffixRange {}
190
191// SAFETY: SuffixRange is safe to share between threads
192// This trait is not automatically implemented because the struct contains a pointer
193unsafe impl Sync for SuffixRange {}
194
195/// A validator that can verify archives with nonlocal memory.
196#[derive(Debug)]
197pub struct ArchiveValidator<'a> {
198    bytes: &'a [u8],
199    subtree_range: Range<*const u8>,
200    subtree_depth: usize,
201    max_subtree_depth: usize,
202}
203
204// SAFETY: ArchiveValidator is safe to send to another thread
205// This trait is not automatically implemented because the struct contains a pointer
206unsafe impl<'a> Send for ArchiveValidator<'a> {}
207
208// SAFETY: ArchiveValidator is safe to share between threads
209// This trait is not automatically implemented because the struct contains a pointer
210unsafe impl<'a> Sync for ArchiveValidator<'a> {}
211
212impl<'a> ArchiveValidator<'a> {
213    /// Creates a new bounds validator for the given bytes.
214    #[inline]
215    pub fn new(bytes: &'a [u8]) -> Self {
216        Self::with_max_depth(bytes, usize::MAX)
217    }
218
219    /// Crates a new bounds validator for the given bytes with a maximum validation depth.
220    #[inline]
221    pub fn with_max_depth(bytes: &'a [u8], max_subtree_depth: usize) -> Self {
222        Self {
223            bytes,
224            subtree_range: bytes.as_ptr_range(),
225            subtree_depth: 0,
226            max_subtree_depth,
227        }
228    }
229
230    /// Returns the log base 2 of the alignment of the archive.
231    ///
232    /// An archive that is 2-aligned will return 1, 4-aligned will return 2, 8-aligned will return 3
233    /// and so on.
234    #[inline]
235    pub fn log_alignment(&self) -> usize {
236        (self.bytes.as_ptr() as usize).trailing_zeros() as usize
237    }
238
239    /// Returns the alignment of the archive.
240    #[inline]
241    pub fn alignment(&self) -> usize {
242        1 << self.log_alignment()
243    }
244}
245
246impl<'a> Fallible for ArchiveValidator<'a> {
247    type Error = ArchiveError;
248}
249
250impl<'a> ArchiveContext for ArchiveValidator<'a> {
251    type PrefixRange = PrefixRange;
252    type SuffixRange = SuffixRange;
253
254    #[inline]
255    unsafe fn bounds_check_ptr(
256        &mut self,
257        base: *const u8,
258        offset: isize,
259    ) -> Result<*const u8, Self::Error> {
260        let base_pos = base.offset_from(self.bytes.as_ptr());
261        let target_pos = base_pos
262            .checked_add(offset)
263            .ok_or(ArchiveError::Overflow { base, offset })?;
264        if target_pos < 0 || target_pos as usize > self.bytes.len() {
265            Err(ArchiveError::OutOfBounds {
266                base,
267                offset,
268                range: self.bytes.as_ptr_range(),
269            })
270        } else {
271            Ok(base.offset(offset))
272        }
273    }
274
275    #[inline]
276    unsafe fn bounds_check_layout(
277        &mut self,
278        data_address: *const u8,
279        layout: &Layout,
280    ) -> Result<(), Self::Error> {
281        if self.alignment() < layout.align() {
282            Err(ArchiveError::Underaligned {
283                expected_align: layout.align(),
284                actual_align: self.alignment(),
285            })
286        } else if (data_address as usize) & (layout.align() - 1) != 0 {
287            Err(ArchiveError::Unaligned {
288                ptr: data_address,
289                align: layout.align(),
290            })
291        } else {
292            let available_space = self.bytes.as_ptr_range().end.offset_from(data_address) as usize;
293            if available_space < layout.size() {
294                Err(ArchiveError::Overrun {
295                    ptr: data_address,
296                    size: layout.size(),
297                    range: self.bytes.as_ptr_range(),
298                })
299            } else {
300                Ok(())
301            }
302        }
303    }
304
305    #[inline]
306    unsafe fn bounds_check_subtree_ptr_layout(
307        &mut self,
308        data_address: *const u8,
309        layout: &Layout,
310    ) -> Result<(), Self::Error> {
311        if layout.size() == 0 {
312            if data_address < self.subtree_range.start || data_address > self.subtree_range.end {
313                Err(ArchiveError::SubtreePointerOutOfBounds {
314                    ptr: data_address,
315                    subtree_range: self.subtree_range.clone(),
316                })
317            } else {
318                Ok(())
319            }
320        } else if !self.subtree_range.contains(&data_address) {
321            Err(ArchiveError::SubtreePointerOutOfBounds {
322                ptr: data_address,
323                subtree_range: self.subtree_range.clone(),
324            })
325        } else {
326            let available_space = self.subtree_range.end.offset_from(data_address) as usize;
327            if available_space < layout.size() {
328                Err(ArchiveError::SubtreePointerOverrun {
329                    ptr: data_address,
330                    size: layout.size(),
331                    subtree_range: self.subtree_range.clone(),
332                })
333            } else {
334                Ok(())
335            }
336        }
337    }
338
339    #[inline]
340    unsafe fn push_prefix_subtree_range(
341        &mut self,
342        root: *const u8,
343        end: *const u8,
344    ) -> Result<PrefixRange, Self::Error> {
345        if self.subtree_depth >= self.max_subtree_depth {
346            Err(ArchiveError::ExceededMaximumSubtreeDepth {
347                max_subtree_depth: self.max_subtree_depth,
348            })
349        } else {
350            let result = PrefixRange {
351                range: Range {
352                    start: end,
353                    end: self.subtree_range.end,
354                },
355                depth: self.subtree_depth,
356            };
357            self.subtree_depth += 1;
358            self.subtree_range.end = root;
359            Ok(result)
360        }
361    }
362
363    #[inline]
364    fn pop_prefix_range(&mut self, range: PrefixRange) -> Result<(), Self::Error> {
365        if self.subtree_depth - 1 != range.depth {
366            Err(ArchiveError::RangePoppedOutOfOrder {
367                expected_depth: self.subtree_depth - 1,
368                actual_depth: range.depth,
369            })
370        } else {
371            self.subtree_range = range.range;
372            self.subtree_depth = range.depth;
373            Ok(())
374        }
375    }
376
377    #[inline]
378    unsafe fn push_suffix_subtree_range(
379        &mut self,
380        start: *const u8,
381        root: *const u8,
382    ) -> Result<SuffixRange, Self::Error> {
383        let result = SuffixRange {
384            start: self.subtree_range.start,
385            depth: self.subtree_depth,
386        };
387        self.subtree_depth += 1;
388        self.subtree_range.start = start;
389        self.subtree_range.end = root;
390        Ok(result)
391    }
392
393    #[inline]
394    fn pop_suffix_range(&mut self, range: SuffixRange) -> Result<(), Self::Error> {
395        if self.subtree_depth - 1 != range.depth {
396            Err(ArchiveError::RangePoppedOutOfOrder {
397                expected_depth: self.subtree_depth - 1,
398                actual_depth: range.depth,
399            })
400        } else {
401            self.subtree_range.end = self.subtree_range.start;
402            self.subtree_range.start = range.start;
403            self.subtree_depth = range.depth;
404            Ok(())
405        }
406    }
407
408    #[inline]
409    fn finish(&mut self) -> Result<(), Self::Error> {
410        if self.subtree_depth != 0 {
411            Err(ArchiveError::UnpoppedSubtreeRanges {
412                last_range: self.subtree_depth - 1,
413            })
414        } else {
415            Ok(())
416        }
417    }
418}