Skip to main content

miri/
provenance_gc.rs

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