| 1 | //! Utility for helping miri understand our exposed pointers. |
| 2 | //! |
| 3 | //! During normal execution, this module is equivalent to pointer casts. However, when running |
| 4 | //! under miri, pointer casts are replaced with lookups in a hash map. This makes Tokio compatible |
| 5 | //! with strict provenance when running under miri (which comes with a performance cost). |
| 6 | |
| 7 | use std::marker::PhantomData; |
| 8 | #[cfg (miri)] |
| 9 | use {crate::loom::sync::Mutex, std::collections::BTreeMap}; |
| 10 | |
| 11 | pub(crate) struct PtrExposeDomain<T> { |
| 12 | #[cfg (miri)] |
| 13 | map: Mutex<BTreeMap<usize, *const T>>, |
| 14 | _phantom: PhantomData<T>, |
| 15 | } |
| 16 | |
| 17 | // SAFETY: Actually using the pointers is unsafe, so it's sound to transfer them across threads. |
| 18 | unsafe impl<T> Sync for PtrExposeDomain<T> {} |
| 19 | |
| 20 | impl<T> PtrExposeDomain<T> { |
| 21 | pub(crate) const fn new() -> Self { |
| 22 | Self { |
| 23 | #[cfg (miri)] |
| 24 | map: Mutex::const_new(BTreeMap::new()), |
| 25 | _phantom: PhantomData, |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | #[inline ] |
| 30 | pub(crate) fn expose_provenance(&self, ptr: *const T) -> usize { |
| 31 | #[cfg (miri)] |
| 32 | { |
| 33 | // FIXME: Use `pointer:addr` when it is stable. |
| 34 | // SAFETY: Equivalent to `pointer::addr` which is safe. |
| 35 | let addr: usize = unsafe { std::mem::transmute(ptr) }; |
| 36 | self.map.lock().insert(addr, ptr); |
| 37 | addr |
| 38 | } |
| 39 | |
| 40 | #[cfg (not(miri))] |
| 41 | { |
| 42 | ptr as usize |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | #[inline ] |
| 47 | #[allow (clippy::wrong_self_convention)] // mirrors std name |
| 48 | pub(crate) fn from_exposed_addr(&self, addr: usize) -> *const T { |
| 49 | #[cfg (miri)] |
| 50 | { |
| 51 | let maybe_ptr = self.map.lock().get(&addr).copied(); |
| 52 | |
| 53 | // SAFETY: Intentionally trigger a miri failure if the provenance we want is not |
| 54 | // exposed. |
| 55 | unsafe { maybe_ptr.unwrap_unchecked() } |
| 56 | } |
| 57 | |
| 58 | #[cfg (not(miri))] |
| 59 | { |
| 60 | addr as *const T |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | #[inline ] |
| 65 | pub(crate) fn unexpose_provenance(&self, _ptr: *const T) { |
| 66 | #[cfg (miri)] |
| 67 | { |
| 68 | // SAFETY: Equivalent to `pointer::addr` which is safe. |
| 69 | let addr: usize = unsafe { std::mem::transmute(_ptr) }; |
| 70 | let maybe_ptr = self.map.lock().remove(&addr); |
| 71 | |
| 72 | // SAFETY: Intentionally trigger a miri failure if the provenance we want is not |
| 73 | // exposed. |
| 74 | unsafe { maybe_ptr.unwrap_unchecked() }; |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | |