Skip to main content

miri/
meta_obj.rs

1use std::any::Any;
2use std::{fmt, iter};
3
4use rustc_abi::Size;
5
6use crate::*;
7
8/// The byte that will be filled into the memory that backs an opaque meta object.
9/// Can be basically anything, the only constraint is that we must be able to tell
10/// it apart from static initializers to avoid allowing a destryed object to be
11/// implicitly re-initialized. Therefore, this must *not* be 0x00.
12pub const OPAQUE_OBJ_COOKIE: u8 = 0x42;
13
14/// A trait for the synchronization metadata that can be attached to a memory location.
15/// Must *not* contain any provenance!
16pub trait MetaObj: Any {
17    /// Determines whether reads/writes to this object's location are currently permitted.
18    fn on_access<'tcx>(&self) -> InterpResult<'tcx> {
19        interp_ok(())
20    }
21
22    /// Determines whether this object is opaque. Opaque objects have their memory contents
23    /// reset to "uninit" on creation, and are implicitly removed on writes.
24    fn opaque(&self) -> bool;
25}
26
27impl dyn MetaObj {
28    #[inline(always)]
29    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
30        let x: &dyn Any = self;
31        x.downcast_ref()
32    }
33
34    #[inline(always)]
35    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
36        let x: &mut dyn Any = self;
37        x.downcast_mut()
38    }
39
40    #[inline(always)]
41    pub fn downcast<T: Any>(self: Box<Self>) -> Option<Box<T>> {
42        let x: Box<dyn Any> = self;
43        x.downcast().ok()
44    }
45}
46
47impl fmt::Debug for dyn MetaObj {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.debug_struct("MetaObj").finish_non_exhaustive()
50    }
51}
52
53impl<'tcx> AllocExtra<'tcx> {
54    /// Get the meta object that starts at the given offset.
55    fn get_meta<T: MetaObj>(&self, offset: Size) -> Option<&T> {
56        self.meta_objs.get_start(offset).and_then(|s| s.downcast_ref::<T>())
57    }
58
59    /// Get the meta object that starts at the given offset.
60    fn get_meta_mut<T: MetaObj>(&mut self, offset: Size) -> Option<&mut T> {
61        self.meta_objs.get_start_mut(offset).and_then(|s| s.downcast_mut::<T>())
62    }
63
64    /// Insert a new meta object covering the given range. Any other objects that exist
65    /// in that range are removed.
66    fn insert_meta(&mut self, range: AllocRange, obj: Box<dyn MetaObj>) {
67        use data_structures::range_object_map::AccessType;
68        let pos = match self.meta_objs.access_type(range) {
69            AccessType::PerfectlyOverlapping(pos) => {
70                self.meta_objs.remove_from_pos(pos);
71                pos
72            }
73            AccessType::Empty(pos) => pos,
74            AccessType::ImperfectlyOverlapping(pos_range) => {
75                self.meta_objs.remove_pos_range(pos_range.clone());
76                pos_range.start
77            }
78        };
79        self.meta_objs.insert_at_pos(pos, range, obj);
80    }
81
82    fn remove_meta(&mut self, range: AllocRange) -> Option<Box<dyn MetaObj>> {
83        use data_structures::range_object_map::AccessType;
84        match self.meta_objs.access_type(range) {
85            AccessType::PerfectlyOverlapping(pos) => Some(self.meta_objs.remove_from_pos(pos)),
86            _ => None,
87        }
88    }
89}
90
91#[derive(Debug, Copy, Clone)]
92pub enum GetMetaError {
93    /// There is no memory behind this pointer.
94    NoMem,
95    /// There is no object stored at this location.
96    NoObj,
97}
98
99impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
100pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
101    /// Get the meta object that starts at the given offset.
102    fn try_get_meta<'a, T: MetaObj>(&'a self, ptr: Pointer) -> Result<&'a T, GetMetaError>
103    where
104        'tcx: 'a,
105    {
106        let ecx = self.eval_context_ref();
107        let Ok((alloc_id, offset, ..)) = ecx.ptr_try_get_alloc_id(ptr, 0) else {
108            return Err(GetMetaError::NoMem);
109        };
110        let info = ecx.get_alloc_info(alloc_id);
111        if info.kind != AllocKind::LiveData || offset >= info.size {
112            return Err(GetMetaError::NoMem);
113        }
114        // This cannot fail now.
115        let alloc_extra = ecx.get_alloc_extra(alloc_id).unwrap();
116        alloc_extra.get_meta::<T>(offset).ok_or(GetMetaError::NoObj)
117    }
118
119    fn insert_meta(&mut self, place: &MPlaceTy<'tcx>, obj: Box<dyn MetaObj>) -> InterpResult<'tcx> {
120        let ecx = self.eval_context_mut();
121        assert!(place.layout.size > Size::ZERO);
122
123        // Ensure there is memory here, and reset it for opaque objects.
124        // We reset to something initialized so that e.g. a typed copy of a `pthread_mutex_t`
125        // does not raise UB due to an invalid value.
126        if obj.opaque() {
127            ecx.write_bytes_ptr(
128                place.ptr(),
129                iter::repeat_n(OPAQUE_OBJ_COOKIE, place.layout.size.bytes_usize()),
130            )?;
131        } else {
132            ecx.check_ptr_access(place.ptr(), place.layout.size, CheckInAllocMsg::MemoryAccess)?;
133        }
134
135        // Now we can insert the object.
136        let (alloc_id, offset, ..) = ecx.ptr_try_get_alloc_id(place.ptr(), 0).unwrap();
137        let (alloc_extra, _machine) = ecx.get_alloc_extra_mut(alloc_id)?;
138        alloc_extra.insert_meta(AllocRange { start: offset, size: place.layout.size }, obj);
139        interp_ok(())
140    }
141
142    fn get_or_insert_meta<'a, T: MetaObj>(
143        &'a mut self,
144        place: &MPlaceTy<'tcx>,
145        obj: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
146    ) -> InterpResult<'tcx, &'a mut T>
147    where
148        'tcx: 'a,
149    {
150        let ecx = self.eval_context_mut();
151        let (alloc_id, offset, ..) = ecx.ptr_get_alloc_id(place.ptr(), 0)?;
152        // Sadly, without Polonius, we cannot reuse `get_meta`, and we do some double-lookups.
153        let alloc_extra = ecx.get_alloc_extra(alloc_id)?;
154        if alloc_extra.get_meta::<T>(offset).is_none() {
155            // No object yet. Create one.
156            let obj = obj(ecx)?;
157            ecx.insert_meta(place, Box::new(obj))?;
158        }
159        // Now there is an object.
160        let (alloc_extra, _machine) = ecx.get_alloc_extra_mut(alloc_id).unwrap();
161        interp_ok(alloc_extra.get_meta_mut(offset).unwrap())
162    }
163
164    fn remove_meta<'a, T: MetaObj>(
165        &'a mut self,
166        place: &MPlaceTy<'tcx>,
167    ) -> InterpResult<'tcx, Option<Box<T>>> {
168        let ecx = self.eval_context_mut();
169        let (alloc_id, offset, ..) = ecx.ptr_get_alloc_id(place.ptr(), 0)?;
170        let (alloc_extra, _machine) = ecx.get_alloc_extra_mut(alloc_id).unwrap();
171
172        interp_ok(
173            alloc_extra
174                .remove_meta(AllocRange { start: offset, size: place.layout.size })
175                .and_then(|obj| obj.downcast()),
176        )
177    }
178}