rkyv/validation/validators/
archive.rs

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