1use 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
23pub trait DynContext {
25 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 unsafe fn bounds_check_layout_dyn(
51 &mut self,
52 data_address: *const u8,
53 layout: &Layout,
54 ) -> Result<(), Box<dyn Error>>;
55
56 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 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 fn pop_prefix_range_dyn(&mut self, range: Box<dyn Any>) -> Result<(), Box<dyn Error>>;
93
94 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 fn pop_suffix_range_dyn(&mut self, range: Box<dyn Any>) -> Result<(), Box<dyn Error>>;
115
116 fn finish_dyn(&mut self) -> Result<(), Box<dyn Error>>;
122
123 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
211pub 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#[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#[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#[derive(Debug)]
372pub enum DynMetadataError {
373 InvalidImplId(u64),
375 MismatchedCachedVtable {
377 type_id: u64,
379 expected: usize,
381 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#[derive(Debug)]
452pub enum CheckDynError {
453 InvalidMetadata(u64),
455 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}