Skip to main content

rapx/analysis/alias/default/
stmt.rs

1use crate::analysis::alias::default::types::ValueKind;
2use crate::analysis::alias::observer::AliasObserver;
3use crate::analysis::points_to::slot::{AbstractLoc, Slot};
4use rustc_abi::FieldIdx;
5use rustc_hir::def_id::DefId;
6use rustc_middle::mir::{AggregateKind, Operand, Rvalue, StatementKind, TerminatorKind};
7use rustc_span::Span;
8
9use super::MopFnAliasMap;
10use super::graph::AliasGraph;
11
12impl<'tcx> AliasGraph<'tcx> {
13    pub fn init_pts_graph(&mut self) {
14        self.pts_graph = crate::analysis::points_to::builder::from_body(self.tcx(), self.def_id());
15        for val in self.values.iter_mut() {
16            let slot = crate::analysis::points_to::slot::Slot::new(val.local);
17            if let Some(si) = self.pts_graph.get_slot_idx(&slot) {
18                val.slot_idx = Some(si);
19            }
20        }
21    }
22
23    /// Unified basic-block MIR processing, shared by MoP alias and SafeDrop.
24    pub fn alias_bb(&mut self, bb_index: usize, obs: &mut dyn AliasObserver) {
25        let body = self.tcx().optimized_mir(self.def_id());
26        let bb = &body.basic_blocks[rustc_middle::mir::BasicBlock::from(bb_index)];
27
28        for stmt in &bb.statements {
29            let span = stmt.source_info.span;
30            if let StatementKind::Assign(assign) = &stmt.kind {
31                let (place, rvalue) = &**assign;
32                self.process_assignment(place, rvalue, span, obs);
33            }
34        }
35    }
36
37    fn process_assignment(
38        &mut self,
39        place: &rustc_middle::mir::Place<'tcx>,
40        rvalue: &rustc_middle::mir::Rvalue<'tcx>,
41        span: Span,
42        obs: &mut dyn AliasObserver,
43    ) {
44        let lv_slot = Slot::from_mir_place(place);
45        let lv_pts = self.pts_graph.ensure_slot(lv_slot.clone(), false, false);
46        if !self.pts_graph.may_drop(lv_pts) {
47            return;
48        }
49        let lv_val = self.projection(*place);
50        self.clear_owner_transfers(lv_val);
51
52        match rvalue {
53            Rvalue::Use(operand, ..) => match operand {
54                Operand::Copy(rv_place) => {
55                    if let Some((rv_val, rv_pts)) = self.resolve_operand(rv_place) {
56                        obs.on_value_use(self, rv_val, span, false);
57                        self.pts_graph.assign_value(lv_pts, rv_pts);
58                        obs.on_value_assign(self, lv_val);
59                    }
60                }
61                Operand::Move(rv_place) => {
62                    if let Some((rv_val, rv_pts)) = self.resolve_operand(rv_place) {
63                        self.owner_transfers.insert(lv_val, rv_val);
64                        obs.on_value_use(self, rv_val, span, false);
65                        if obs.track_all_moves()
66                            || self.pts_graph.slot_kind(rv_pts) == ValueKind::RawPtr
67                        {
68                            self.pts_graph.assign_value(lv_pts, rv_pts);
69                        }
70                        obs.on_value_assign(self, lv_val);
71                    }
72                }
73                Operand::Constant(_) => {}
74                #[cfg(rapx_ge_95)]
75                Operand::RuntimeChecks(_) => {}
76            },
77            Rvalue::Ref(_, _, rv_place)
78            | Rvalue::RawPtr(_, rv_place)
79            | Rvalue::CopyForDeref(rv_place) => {
80                let rv_slot = Slot::from_mir_place(rv_place);
81                if let Some(rv_val) = self.place_to_value_idx(rv_place) {
82                    obs.on_value_use(self, rv_val, span, false);
83                }
84                let rv_slot_clone = rv_slot.clone();
85                self.pts_graph
86                    .assign_pointee(lv_pts, AbstractLoc::Slot(rv_slot));
87                if let Some(rv_pts) = self.pts_graph.get_slot_idx(&rv_slot_clone) {
88                    self.pts_graph.merge_equivalence(lv_pts, rv_pts);
89                }
90                obs.on_value_assign(self, lv_val);
91            }
92            Rvalue::Cast(_, operand, _) => match operand {
93                Operand::Copy(rv_place) | Operand::Move(rv_place) => {
94                    if let Some((rv_val, rv_pts)) = self.resolve_operand(rv_place) {
95                        obs.on_value_use(self, rv_val, span, false);
96                        self.pts_graph.assign_value(lv_pts, rv_pts);
97                        obs.on_value_assign(self, lv_val);
98                    }
99                }
100                _ => {}
101            },
102            Rvalue::Aggregate(kind, operands) => {
103                match kind.as_ref() {
104                    AggregateKind::Tuple | AggregateKind::Adt(..) => {
105                        for (field_idx, operand) in operands.iter_enumerated() {
106                            match operand {
107                                Operand::Copy(rv_place) | Operand::Move(rv_place) => {
108                                    if let Some((rv_val, rv_pts)) = self.resolve_operand(rv_place) {
109                                        let field_slot = lv_slot.project(field_idx.as_usize());
110                                        let field_pts =
111                                            self.pts_graph.ensure_slot(field_slot, false, false);
112                                        // Ensure value entry exists for the field
113                                        let field_val = self.projection_field(place, field_idx);
114                                        obs.on_value_use(self, rv_val, span, false);
115                                        self.pts_graph.assign_value(field_pts, rv_pts);
116                                        obs.on_value_assign(self, field_val);
117                                    }
118                                }
119                                _ => {}
120                            }
121                        }
122                    }
123                    _ => {
124                        for operand in operands {
125                            match operand {
126                                Operand::Copy(rv_place) | Operand::Move(rv_place) => {
127                                    if let Some((rv_val, rv_pts)) = self.resolve_operand(rv_place) {
128                                        obs.on_value_use(self, rv_val, span, false);
129                                        self.pts_graph.assign_value(lv_pts, rv_pts);
130                                        obs.on_value_assign(self, lv_val);
131                                    }
132                                }
133                                _ => {}
134                            }
135                        }
136                    }
137                }
138            }
139            #[cfg(not(rapx_ge_99))]
140            Rvalue::ShallowInitBox(operand, _) => match operand {
141                Operand::Copy(rv_place) | Operand::Move(rv_place) => {
142                    if let Some((rv_val, rv_pts)) = self.resolve_operand(rv_place) {
143                        obs.on_value_use(self, rv_val, span, false);
144                        self.pts_graph.assign_value(lv_pts, rv_pts);
145                        obs.on_value_assign(self, lv_val);
146                    }
147                }
148                _ => {}
149            },
150            Rvalue::Discriminant(rv_place) => {
151                if let Some((rv_val, rv_pts)) = self.resolve_operand(rv_place) {
152                    obs.on_value_use(self, rv_val, span, false);
153                    self.pts_graph.assign_value(lv_pts, rv_pts);
154                    obs.on_value_assign(self, lv_val);
155                }
156            }
157            _ => {}
158        }
159    }
160
161    /// Resolve a MIR Place to (value_index, pts_slot_index) if it may drop.
162    fn resolve_operand(
163        &mut self,
164        place: &rustc_middle::mir::Place<'tcx>,
165    ) -> Option<(usize, usize)> {
166        let slot = Slot::from_mir_place(place);
167        let pts_idx = self.pts_graph.ensure_slot(slot, false, false);
168        if !self.pts_graph.may_drop(pts_idx) {
169            return None;
170        }
171        let val_idx = self.projection(*place);
172        Some((val_idx, pts_idx))
173    }
174
175    /// Lookup value index for a place without dynamic creation.
176    fn place_to_value_idx(&self, place: &rustc_middle::mir::Place<'tcx>) -> Option<usize> {
177        let slot = Slot::from_mir_place(place);
178        self.slot_to_value_idx(&slot)
179    }
180
181    /// Ensure value entry exists for a field projection.
182    fn projection_field(
183        &mut self,
184        base: &rustc_middle::mir::Place<'tcx>,
185        field_idx: FieldIdx,
186    ) -> usize {
187        let body = self.tcx().optimized_mir(self.def_id());
188        let base_ty = base.ty(&body.local_decls, self.tcx()).ty;
189        let field_ty = match base_ty.kind() {
190            rustc_middle::ty::TyKind::Tuple(fields) => {
191                fields.get(field_idx.as_usize()).copied().unwrap_or(base_ty)
192            }
193            rustc_middle::ty::TyKind::Adt(..) => base_ty,
194            _ => base_ty,
195        };
196        let field_place = self.tcx().mk_place_field(*base, field_idx, field_ty);
197        self.projection(field_place)
198    }
199
200    /// Reverse lookup: given a Slot, find the corresponding value index.
201    pub fn slot_to_value_idx(&self, slot: &Slot) -> Option<usize> {
202        if slot.fields.is_empty() {
203            let local = slot.local;
204            if local < self.values.len() {
205                return Some(local);
206            }
207            return None;
208        }
209        let mut cur = slot.local;
210        if cur >= self.values.len() {
211            return None;
212        }
213        for &field_id in &slot.fields {
214            cur = *self.values[cur].fields.get(&field_id)?;
215        }
216        Some(cur)
217    }
218
219    /// Unified call-site processing, shared by MoP alias and SafeDrop.
220    pub fn alias_bbcall(
221        &mut self,
222        bb_index: usize,
223        fn_map: &MopFnAliasMap,
224        obs: &mut dyn AliasObserver,
225    ) {
226        let (merge_slots, ret_local, may_drop_count, target_id, span) =
227            self.parse_call_slots(bb_index);
228        if merge_slots.is_empty() {
229            return;
230        }
231        self.clear_owner_transfers(merge_slots[0].0);
232
233        // UAF check for arguments (skip return-value slot at index 0)
234        for &(val_idx, _) in merge_slots.iter().skip(1) {
235            if val_idx != 0 {
236                obs.on_value_use(self, val_idx, span, true);
237            }
238        }
239
240        if may_drop_count <= 1 {
241            let (ret_val, ret_slot) = merge_slots[0];
242            if ret_val != 0 && self.pts_graph.may_drop(ret_slot) {
243                self.pts_graph.reset_partition(ret_slot);
244                obs.on_value_assign(self, ret_val);
245            }
246            return;
247        }
248
249        match target_id {
250            Some(id) if super::alias::is_no_alias_intrinsic(id) => {}
251            Some(id) if !self.tcx().is_mir_available(id) => {
252                let (ret_val, _) = merge_slots[0];
253                if ret_val != 0 && self.value_is_ptr(ret_val) {
254                    let slot_args: Vec<usize> = merge_slots.iter().map(|&(_, s)| s).collect();
255                    self.pts_graph.conservative_call_merge(&slot_args);
256                    obs.on_value_assign(self, ret_val);
257                }
258            }
259            Some(id) => self.apply_fn_alias_results_pts(id, &merge_slots, fn_map, obs),
260            None => {
261                let (ret_val, _) = merge_slots[0];
262                if ret_val != 0
263                    && self
264                        .pts_graph
265                        .get_slot_idx(&Slot::new(ret_local))
266                        .is_some_and(|si| self.pts_graph.slot_is_ptr(si))
267                {
268                    let slot_args: Vec<usize> = merge_slots.iter().map(|&(_, s)| s).collect();
269                    self.pts_graph.conservative_call_merge(&slot_args);
270                    obs.on_value_assign(self, ret_val);
271                }
272            }
273        }
274
275        let (ret_val, _) = merge_slots[0];
276        if ret_val != 0 && self.pts_graph.may_drop(merge_slots[0].1) {
277            obs.on_value_assign(self, ret_val);
278        }
279    }
280
281    /// Parse call terminator, returning slot-based merge info.
282    fn parse_call_slots(
283        &mut self,
284        bb_index: usize,
285    ) -> (Vec<(usize, usize)>, usize, usize, Option<DefId>, Span) {
286        let terminator = match self.terminator(bb_index) {
287            Some(t) => t.clone(),
288            None => return (vec![], 0, 0, None, rustc_span::DUMMY_SP),
289        };
290        let TerminatorKind::Call {
291            func: ref func_op,
292            ref args,
293            ref destination,
294            ..
295        } = terminator.kind
296        else {
297            return (vec![], 0, 0, None, rustc_span::DUMMY_SP);
298        };
299        let span = terminator.source_info.span;
300
301        let target_id = match func_op {
302            Operand::Constant(c) => match c.ty().kind() {
303                rustc_middle::ty::FnDef(id, _) => Some(*id),
304                _ => None,
305            },
306            _ => None,
307        };
308
309        let ret_local = destination.local.as_usize();
310        let ret_slot = Slot::new(ret_local);
311        let ret_pts = self.pts_graph.ensure_slot(ret_slot, false, false);
312        let ret_val = self.projection(*destination);
313        let mut result = vec![(ret_val, ret_pts)];
314        let mut may_drop_count: usize = if self.pts_graph.may_drop(ret_pts) {
315            1
316        } else {
317            0
318        };
319
320        for arg in args {
321            match arg.node {
322                Operand::Copy(ref p) | Operand::Move(ref p) => {
323                    let arg_local = p.local.as_usize();
324                    let arg_slot = Slot::new(arg_local);
325                    let arg_pts = self.pts_graph.ensure_slot(arg_slot, false, false);
326                    let arg_val = self.projection(*p);
327                    if self.pts_graph.may_drop(arg_pts) {
328                        may_drop_count += 1;
329                    }
330                    result.push((arg_val, arg_pts));
331                }
332                Operand::Constant(_) => {
333                    result.push((0, 0));
334                }
335                #[cfg(rapx_ge_95)]
336                Operand::RuntimeChecks(_) => {}
337            }
338        }
339
340        (result, ret_local, may_drop_count, target_id, span)
341    }
342
343    fn apply_fn_alias_results_pts(
344        &mut self,
345        target_id: DefId,
346        merge_vec: &[(usize, usize)],
347        fn_map: &MopFnAliasMap,
348        obs: &mut dyn AliasObserver,
349    ) {
350        let Some(fn_aliases) = fn_map.get(&target_id) else {
351            return;
352        };
353        if fn_aliases.aliases().is_empty() {
354            return;
355        }
356        let unified: crate::analysis::alias::FnAliasPairs = From::from(fn_aliases.clone());
357        let slot_args: Vec<usize> = merge_vec.iter().map(|&(_, s)| s).collect();
358        self.pts_graph.apply_callee_summary(&unified, &slot_args);
359        obs.on_state_change(self);
360    }
361
362    pub fn merge_results_pts(&mut self) {
363        let pairs = self.pts_graph.fn_alias_pairs(self.arg_size());
364        for alias in pairs.aliases() {
365            let lv_local = alias.left_local();
366            let rv_local = alias.right_local();
367            let lv_slot = self.value_to_slot_idx(lv_local).unwrap_or(lv_local);
368            let rv_slot = self.value_to_slot_idx(rv_local).unwrap_or(rv_local);
369            let mut mop_alias = super::MopAliasPair::new(
370                alias.left_local(),
371                self.pts_graph.may_drop(lv_slot),
372                self.pts_graph.need_drop(lv_slot),
373                alias.right_local(),
374                self.pts_graph.may_drop(rv_slot),
375                self.pts_graph.need_drop(rv_slot),
376            );
377            mop_alias.fact = alias.clone();
378            self.ret_alias.add_alias(mop_alias);
379        }
380    }
381}