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#[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 #[inline]
108 pub fn new(bytes: &'a [u8]) -> Self {
109 Self::with_max_depth(bytes, None)
110 }
111
112 #[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}
192
193#[cfg(test)]
194mod tests {
195 use rancor::Error;
196
197 use crate::{
198 api::low::{access, access_pos},
199 boxed::ArchivedBox,
200 option::ArchivedOption,
201 util::Align,
202 Archived,
203 };
204
205 #[test]
206 fn basic_functionality() {
207 #[cfg(all(feature = "pointer_width_16", not(feature = "big_endian")))]
208 let synthetic_buf = Align([
210 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
212 0u8, 1u8, 0u8, 0xf2u8, 0xffu8, 11u8, 0u8, ]);
217
218 #[cfg(all(feature = "pointer_width_16", feature = "big_endian"))]
219 let synthetic_buf = Align([
221 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
223 0u8, 1u8, 0u8, 0xffu8, 0xf2u8, 0u8, 11u8, ]);
228
229 #[cfg(all(
230 not(any(
231 feature = "pointer_width_16",
232 feature = "pointer_width_64",
233 )),
234 not(feature = "big_endian"),
235 ))]
236 let synthetic_buf = Align([
238 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
240 0u8, 1u8, 0u8, 0u8, 0u8, 0xf0u8, 0xffu8, 0xffu8, 0xffu8, 11u8, 0u8, 0u8, 0u8, ]);
245
246 #[cfg(all(
247 not(any(
248 feature = "pointer_width_16",
249 feature = "pointer_width_64",
250 )),
251 feature = "big_endian",
252 ))]
253 let synthetic_buf = Align([
255 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
257 0u8, 1u8, 0u8, 0u8, 0u8, 0xffu8, 0xffu8, 0xffu8, 0xf0u8, 0u8, 0u8, 0u8, 11u8, ]);
262
263 #[cfg(all(feature = "pointer_width_64", not(feature = "big_endian")))]
264 let synthetic_buf = Align([
266 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
268 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0xe8u8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8,
272 11u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
273 0u8, ]);
275
276 #[cfg(all(feature = "pointer_width_64", feature = "big_endian"))]
277 let synthetic_buf = Align([
279 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
281 0x21, 0x21, 0x21, 0x21, 0x21, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
282 0u8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xe8u8, 0u8,
285 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
286 11u8, ]);
288
289 let result =
290 access::<ArchivedOption<ArchivedBox<[u8]>>, Error>(&*synthetic_buf);
291 result.unwrap();
292
293 let result =
295 access_pos::<Archived<u32>, Error>(&*Align([0, 1, 2, 3, 4]), 8);
296 assert_source!(
297 result.unwrap_err(),
298 super::InvalidSubtreePointer { size: 4, .. },
299 "error source should be out-of-bounds",
300 );
301 let result =
303 access_pos::<Archived<u32>, Error>(&*Align([0, 1, 2, 3, 4]), 4);
304 assert_source!(
305 result.unwrap_err(),
306 super::InvalidSubtreePointer { size: 4, .. },
307 "error source should be overrun",
308 );
309 let result =
311 access_pos::<Archived<u32>, Error>(&*Align([0, 1, 2, 3, 4]), 1);
312 assert_source!(
313 result.unwrap_err(),
314 super::UnalignedPointer { align: 4, .. },
315 "error source should be unaligned",
316 );
317 }
318
319 #[cfg(not(any(
320 feature = "pointer_width_16",
321 feature = "pointer_width_64"
322 )))]
323 #[test]
324 fn invalid_tags() {
325 let synthetic_buf = Align([
327 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
329 0u8, 2u8, 0u8, 0u8, 0u8, 0xe8, 0xff, 0xff, 0xff, 11u8, 0u8, 0u8, 0u8, ]);
334
335 let result =
336 access::<ArchivedOption<ArchivedBox<[u8]>>, Error>(&*synthetic_buf);
337 assert_source!(
338 result.unwrap_err(),
339 bytecheck::InvalidEnumDiscriminantError::<u8> {
340 enum_name: "ArchivedOption",
341 invalid_discriminant: 2,
342 },
343 "error source should be invalid enum discriminant",
344 );
345 }
346
347 #[cfg(not(any(
348 feature = "pointer_width_16",
349 feature = "pointer_width_64"
350 )))]
351 #[test]
352 fn overlapping_claims() {
353 let synthetic_buf = Align([
355 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
357 0, 0xf0, 0xff, 0xff, 0xff, 11u8, 0u8, 0u8, 0u8, 0xe8, 0xff, 0xff, 0xff, 11u8, 0u8, 0u8, 0u8, ]);
365
366 let result = access::<[ArchivedBox<[u8]>; 2], Error>(&*synthetic_buf);
367 assert_source!(
368 result.unwrap_err(),
369 super::InvalidSubtreePointer { size: 11, .. },
370 "error source should be invalid subtree pointer",
371 );
372 }
373
374 #[cfg(not(any(
375 feature = "pointer_width_16",
376 feature = "pointer_width_64"
377 )))]
378 #[cfg(feature = "alloc")]
379 #[test]
380 fn cycle_detection() {
381 use crate::{
382 ser::Writer, validation::ArchiveContext, Archive, Serialize,
383 };
384
385 #[allow(dead_code)]
386 #[derive(Archive, Serialize)]
387 #[rkyv(
388 crate,
389 serialize_bounds(__S: Writer),
390 bytecheck(bounds(__C: ArchiveContext)),
391 derive(Debug),
392 )]
393 enum Node {
394 Nil,
395 Cons(#[rkyv(omit_bounds)] crate::alloc::boxed::Box<Node>),
396 }
397
398 let synthetic_buf = Align([
400 1u8, 0u8, 0u8, 0u8, 4u8, 0u8, 0u8, 0u8, 1u8, 0u8, 0u8, 0u8, 244u8, 255u8, 255u8, 255u8, ]);
407
408 let result = access::<ArchivedNode, Error>(&*synthetic_buf);
409 assert_source!(
410 result.unwrap_err(),
411 super::InvalidSubtreePointer { .. },
412 "error source should be invalid subtree pointer",
413 );
414 }
415}