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).
67use std::marker::PhantomData;
8#[cfg(miri)]
9use {crate::loom::sync::Mutex, std::collections::BTreeMap};
1011pub(crate) struct PtrExposeDomain<T> {
12#[cfg(miri)]
13map: Mutex<BTreeMap<usize, *const T>>,
14 _phantom: PhantomData<T>,
15}
1617// SAFETY: Actually using the pointers is unsafe, so it's sound to transfer them across threads.
18unsafe impl<T> Sync for PtrExposeDomain<T> {}
1920impl<T> PtrExposeDomain<T> {
21pub(crate) const fn new() -> Self {
22Self {
23#[cfg(miri)]
24map: Mutex::const_new(BTreeMap::new()),
25 _phantom: PhantomData,
26 }
27 }
2829#[inline]
30pub(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.
35let addr: usize = unsafe { std::mem::transmute(ptr) };
36self.map.lock().insert(addr, ptr);
37 addr
38 }
3940#[cfg(not(miri))]
41{
42 ptr as usize
43 }
44 }
4546#[inline]
47 #[allow(clippy::wrong_self_convention)] // mirrors std name
48pub(crate) fn from_exposed_addr(&self, addr: usize) -> *const T {
49#[cfg(miri)]
50{
51let maybe_ptr = self.map.lock().get(&addr).copied();
5253// SAFETY: Intentionally trigger a miri failure if the provenance we want is not
54 // exposed.
55unsafe { maybe_ptr.unwrap_unchecked() }
56 }
5758#[cfg(not(miri))]
59{
60 addr as *const T
61 }
62 }
6364#[inline]
65pub(crate) fn unexpose_provenance(&self, _ptr: *const T) {
66#[cfg(miri)]
67{
68// SAFETY: Equivalent to `pointer::addr` which is safe.
69let addr: usize = unsafe { std::mem::transmute(_ptr) };
70let maybe_ptr = self.map.lock().remove(&addr);
7172// SAFETY: Intentionally trigger a miri failure if the provenance we want is not
73 // exposed.
74unsafe { maybe_ptr.unwrap_unchecked() };
75 }
76 }
77}