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