Skip to main content

miri/
provenance_gc.rs

1use rustc_data_structures::either::Either;
2use rustc_data_structures::fx::FxHashSet;
3
4use crate::*;
5
6pub type VisitWith<'a> = dyn FnMut(Option<AllocId>, Option<BorTag>) + 'a;
7
8pub trait VisitProvenance {
9    fn visit_provenance(&self, visit: &mut VisitWith<'_>);
10}
11
12// Trivial impls for types that do not contain any provenance
13macro_rules! no_provenance {
14    ($($ty:ident)+) => {
15        $(
16            impl VisitProvenance for $ty {
17                fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
18            }
19        )+
20    }
21}
22no_provenance!(i8 i16 i32 i64 isize u8 u16 u32 u64 usize bool ThreadId);
23
24impl VisitProvenance for &'static str {
25    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
26}
27
28impl<T: VisitProvenance> VisitProvenance for Option<T> {
29    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
30        if let Some(x) = self {
31            x.visit_provenance(visit);
32        }
33    }
34}
35
36impl<A, B> VisitProvenance for (A, B)
37where
38    A: VisitProvenance,
39    B: VisitProvenance,
40{
41    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
42        self.0.visit_provenance(visit);
43        self.1.visit_provenance(visit);
44    }
45}
46
47impl<T: VisitProvenance> VisitProvenance for Vec<T> {
48    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
49        self.iter().for_each(|el| el.visit_provenance(visit));
50    }
51}
52
53impl<T: VisitProvenance> VisitProvenance for std::cell::RefCell<T> {
54    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
55        self.borrow().visit_provenance(visit)
56    }
57}
58
59impl VisitProvenance for BorTag {
60    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
61        visit(None, Some(*self))
62    }
63}
64
65impl VisitProvenance for AllocId {
66    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
67        visit(Some(*self), None)
68    }
69}
70
71impl VisitProvenance for Provenance {
72    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
73        if let Provenance::Concrete { alloc_id, tag, .. } = self {
74            visit(Some(*alloc_id), Some(*tag));
75        }
76    }
77}
78
79impl VisitProvenance for StrictPointer {
80    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
81        self.provenance.visit_provenance(visit);
82    }
83}
84
85impl VisitProvenance for Pointer {
86    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
87        self.provenance.visit_provenance(visit);
88    }
89}
90
91impl VisitProvenance for Scalar {
92    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
93        match self {
94            Scalar::Ptr(ptr, _) => ptr.visit_provenance(visit),
95            Scalar::Int(_) => (),
96        }
97    }
98}
99
100impl VisitProvenance for IoError {
101    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
102        use crate::shims::io_error::IoError::*;
103        match self {
104            LibcError(_name) => (),
105            WindowsError(_name) => (),
106            HostError(_io_error) => (),
107            Raw(scalar) => scalar.visit_provenance(visit),
108        }
109    }
110}
111
112impl VisitProvenance for Immediate<Provenance> {
113    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
114        match self {
115            Immediate::Scalar(s) => {
116                s.visit_provenance(visit);
117            }
118            Immediate::ScalarPair(s1, s2) => {
119                s1.visit_provenance(visit);
120                s2.visit_provenance(visit);
121            }
122            Immediate::Uninit => {}
123        }
124    }
125}
126
127impl VisitProvenance for MemPlaceMeta<Provenance> {
128    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
129        match self {
130            MemPlaceMeta::Meta(m) => m.visit_provenance(visit),
131            MemPlaceMeta::None => {}
132        }
133    }
134}
135
136impl VisitProvenance for ImmTy<'_> {
137    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
138        (**self).visit_provenance(visit)
139    }
140}
141
142impl VisitProvenance for MPlaceTy<'_> {
143    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
144        self.ptr().visit_provenance(visit);
145        self.meta().visit_provenance(visit);
146    }
147}
148
149impl VisitProvenance for PlaceTy<'_> {
150    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
151        match self.as_mplace_or_local() {
152            Either::Left(mplace) => mplace.visit_provenance(visit),
153            Either::Right(_) => (),
154        }
155    }
156}
157
158impl VisitProvenance for OpTy<'_> {
159    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
160        match self.as_mplace_or_imm() {
161            Either::Left(mplace) => mplace.visit_provenance(visit),
162            Either::Right(imm) => imm.visit_provenance(visit),
163        }
164    }
165}
166
167impl VisitProvenance for Allocation<Provenance, AllocExtra<'_>, MiriAllocBytes> {
168    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
169        for prov in self.provenance().provenances() {
170            prov.visit_provenance(visit);
171        }
172
173        self.extra.visit_provenance(visit);
174    }
175}
176
177impl VisitProvenance for crate::MiriInterpCx<'_> {
178    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
179        // Visit the contents of the allocations and the IDs themselves, to account for all
180        // live allocation IDs and all provenance in the allocation bytes, even if they are leaked.
181        // We do *not* visit all the `AllocId` of the live allocations; we tried that and adding
182        // them all to the live set is too expensive. Instead we later do liveness check by
183        // checking both "is this alloc id live" and "is it mentioned anywhere else in
184        // the interpreter state".
185        self.memory.alloc_map().iter(|it| {
186            for (_id, (_kind, alloc)) in it {
187                alloc.visit_provenance(visit);
188            }
189        });
190        // And all the other machine values.
191        self.machine.visit_provenance(visit);
192    }
193}
194
195pub struct LiveAllocs<'a, 'tcx> {
196    collected: FxHashSet<AllocId>,
197    ecx: &'a MiriInterpCx<'tcx>,
198}
199
200impl LiveAllocs<'_, '_> {
201    pub fn is_live(&self, id: AllocId) -> bool {
202        self.collected.contains(&id) || self.ecx.is_alloc_live(id)
203    }
204}
205
206fn remove_unreachable_tags<'tcx>(ecx: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
207    // Avoid iterating all allocations if there's no borrow tracker anyway.
208    if ecx.machine.borrow_tracker.is_some() {
209        ecx.memory.alloc_map().iter(|it| {
210            for (_id, (_kind, alloc)) in it {
211                alloc.extra.borrow_tracker.as_ref().unwrap().remove_unreachable_tags(&tags);
212            }
213        });
214    }
215}
216
217fn remove_unreachable_allocs<'tcx>(ecx: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
218    let allocs = LiveAllocs { ecx, collected: allocs };
219    ecx.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
220    ecx.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
221    ecx.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
222    if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
223        borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
224    }
225    // Clean up core (non-Miri-specific) state.
226    ecx.remove_unreachable_allocs(&allocs.collected);
227}
228
229impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
230pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
231    fn run_provenance_gc(&mut self) {
232        let this = self.eval_context_mut();
233
234        // We collect all tags and AllocId from every part of the interpreter.
235        let mut tags = FxHashSet::default();
236        let mut alloc_ids = FxHashSet::default();
237        this.visit_provenance(&mut |id, tag| {
238            if let Some(id) = id {
239                alloc_ids.insert(id);
240            }
241            if let Some(tag) = tag {
242                tags.insert(tag);
243            }
244        });
245
246        // Based on this, clean up the interpreter state.
247        remove_unreachable_tags(this, tags);
248        remove_unreachable_allocs(this, alloc_ids);
249    }
250}