rapx/analysis/safedrop/
safedrop.rs

1use super::graph::*;
2use crate::analysis::core::alias_analysis::default::{MopAAResultMap, block::Term};
3use rustc_middle::{
4    mir::{
5        Operand::{self, Constant, Copy, Move},
6        Place, TerminatorKind,
7    },
8    ty::{TyCtxt, TyKind, TypingEnv},
9};
10use std::{
11    collections::{HashMap, HashSet},
12    vec,
13};
14
15pub const VISIT_LIMIT: usize = 1000;
16
17impl<'tcx> SafeDropGraph<'tcx> {
18    // analyze the drop statement and update the liveness for nodes.
19    pub fn drop_check(&mut self, bb_idx: usize, tcx: TyCtxt<'tcx>) {
20        let cur_block = self.mop_graph.blocks[bb_idx].clone();
21        let is_cleanup = cur_block.is_cleanup;
22        if let Term::Drop(drop) = cur_block.terminator {
23            rap_debug!("drop check bb: {}, {:?}", bb_idx, drop);
24            match drop.kind {
25                TerminatorKind::Drop {
26                    ref place,
27                    target: _,
28                    unwind: _,
29                    replace: _,
30                    drop: _,
31                    async_fut: _,
32                } => {
33                    if !self.drop_heap_item_check(place, tcx) {
34                        return;
35                    }
36                    let birth = self.mop_graph.blocks[bb_idx].scc.enter;
37                    let local = self.projection(false, place.clone());
38                    let info = drop.source_info.clone();
39                    self.add_to_drop_record(local, local, birth, &info, false, bb_idx, is_cleanup);
40                }
41                TerminatorKind::Call {
42                    func: _, ref args, ..
43                } => {
44                    if args.len() > 0 {
45                        let birth = self.mop_graph.blocks[bb_idx].scc.enter;
46                        let place = match args[0].node {
47                            Operand::Copy(place) => place,
48                            Operand::Move(place) => place,
49                            _ => {
50                                rap_error!("Constant operand exists: {:?}", args[0]);
51                                return;
52                            }
53                        };
54                        let local = self.projection(false, place.clone());
55                        let info = drop.source_info.clone();
56                        self.add_to_drop_record(
57                            local, local, birth, &info, false, bb_idx, is_cleanup,
58                        );
59                    }
60                }
61                _ => {}
62            }
63        }
64    }
65
66    pub fn drop_heap_item_check(&self, place: &Place<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
67        let place_ty = place.ty(&tcx.optimized_mir(self.mop_graph.def_id).local_decls, tcx);
68        match place_ty.ty.kind() {
69            TyKind::Adt(adtdef, ..) => match self.adt_owner.get(&adtdef.did()) {
70                None => true,
71                Some(owenr_unit) => {
72                    let idx = match place_ty.variant_index {
73                        Some(vdx) => vdx.index(),
74                        None => 0,
75                    };
76                    if owenr_unit[idx].0.is_onheap() || owenr_unit[idx].1.contains(&true) {
77                        true
78                    } else {
79                        false
80                    }
81                }
82            },
83            _ => true,
84        }
85    }
86
87    pub fn split_check(&mut self, bb_idx: usize, tcx: TyCtxt<'tcx>, fn_map: &MopAAResultMap) {
88        /* duplicate the status before visiteding a path; */
89        let backup_values = self.mop_graph.values.clone(); // duplicate the status when visiteding different paths;
90        let backup_constant = self.mop_graph.constants.clone();
91        let backup_alias_set = self.mop_graph.alias_set.clone();
92        let backup_drop_record = self.drop_record.clone();
93        self.check(bb_idx, tcx, fn_map);
94        /* restore after visited */
95        self.mop_graph.values = backup_values;
96        self.mop_graph.constants = backup_constant;
97        self.mop_graph.alias_set = backup_alias_set;
98        self.drop_record = backup_drop_record;
99    }
100
101    pub fn split_check_with_cond(
102        &mut self,
103        bb_idx: usize,
104        path_discr_id: usize,
105        path_discr_val: usize,
106        tcx: TyCtxt<'tcx>,
107        fn_map: &MopAAResultMap,
108    ) {
109        /* duplicate the status before visiteding a path; */
110        let backup_values = self.mop_graph.values.clone(); // duplicate the status when visiteding different paths;
111        let backup_constant = self.mop_graph.constants.clone();
112        let backup_alias_set = self.mop_graph.alias_set.clone();
113        let backup_drop_record = self.drop_record.clone();
114        /* add control-sensitive indicator to the path status */
115        self.mop_graph
116            .constants
117            .insert(path_discr_id, path_discr_val);
118        self.check(bb_idx, tcx, fn_map);
119        /* restore after visited */
120        self.mop_graph.values = backup_values;
121        self.mop_graph.constants = backup_constant;
122        self.mop_graph.alias_set = backup_alias_set;
123        self.drop_record = backup_drop_record;
124    }
125
126    // the core function of the safedrop.
127    pub fn check(&mut self, bb_idx: usize, tcx: TyCtxt<'tcx>, fn_map: &MopAAResultMap) {
128        self.mop_graph.visit_times += 1;
129        if self.mop_graph.visit_times > VISIT_LIMIT {
130            return;
131        }
132        let scc_idx = self.mop_graph.blocks[bb_idx].scc.enter;
133        let cur_block = self.mop_graph.blocks[bb_idx].clone();
134        rap_debug!(
135            "Checking bb: {}, scc_idx: {}, scc: {:?}",
136            bb_idx,
137            scc_idx,
138            cur_block.scc.clone(),
139        );
140
141        self.alias_bb(self.mop_graph.blocks[bb_idx].scc.enter);
142        self.alias_bbcall(self.mop_graph.blocks[bb_idx].scc.enter, tcx, fn_map);
143        self.drop_check(self.mop_graph.blocks[bb_idx].scc.enter, tcx);
144        if bb_idx == scc_idx {
145            // && !cur_block.scc.nodes.is_empty() {
146            // this should allways true
147            let mut paths_in_scc = vec![];
148
149            /* Handle cases if the current block is a merged scc block with sub block */
150            self.mop_graph.calculate_scc_order(
151                scc_idx,
152                bb_idx,
153                &mut cur_block.scc.clone().nodes,
154                &mut vec![],
155                &mut HashMap::new(),
156                &mut HashSet::new(),
157                &mut paths_in_scc,
158            );
159            rap_debug!("Paths in scc: {:?}", paths_in_scc);
160
161            let backup_values = self.mop_graph.values.clone(); // duplicate the status when visiteding different paths;
162            let backup_constant = self.mop_graph.constants.clone();
163            let backup_alias_set = self.mop_graph.alias_set.clone();
164            for each_path in paths_in_scc {
165                self.mop_graph.alias_set = backup_alias_set.clone();
166                self.mop_graph.values = backup_values.clone();
167                self.mop_graph.constants = backup_constant.clone();
168
169                if !each_path.is_empty() {
170                    for idx in each_path {
171                        self.alias_bb(idx);
172                        self.alias_bbcall(idx, tcx, fn_map);
173                        self.drop_check(idx, tcx);
174                    }
175                }
176            }
177            /* Reach a leaf node, check bugs */
178            rap_debug!("cur_block.next: {:?}", cur_block.next);
179            match cur_block.next.len() {
180                0 => {
181                    if Self::should_check(self.mop_graph.def_id) {
182                        self.dp_check(cur_block.is_cleanup);
183                    }
184                    return;
185                }
186                1 => {
187                    /*
188                     * Equivalent to self.check(cur_block.next[0]..);
189                     * We cannot use [0] for FxHashSet.
190                     */
191                    for next in cur_block.next {
192                        self.check(next, tcx, fn_map);
193                    }
194                    return;
195                }
196                _ => {}
197            }
198        }
199        /* Begin: handle the SwitchInt statement. */
200        let mut single_target = false;
201        let mut sw_val = 0;
202        let mut sw_target = 0; // Single target
203        let mut path_discr_id = 0; // To avoid analyzing paths that cannot be reached with one enum type.
204        let mut sw_targets = None; // Multiple targets of SwitchInt
205        if let Term::Switch(switch) = &cur_block.terminator
206            && cur_block.scc.nodes.is_empty()
207        {
208            rap_debug!("Handle switchInt in bb {:?}", cur_block);
209            if let TerminatorKind::SwitchInt {
210                ref discr,
211                ref targets,
212            } = switch.kind
213            {
214                rap_debug!("{:?}", switch);
215                rap_debug!("{:?}", self.mop_graph.constants);
216                match discr {
217                    Copy(p) | Move(p) => {
218                        let place = self.projection(false, *p);
219                        let local_decls = &tcx.optimized_mir(self.mop_graph.def_id).local_decls;
220                        let place_ty = (*p).ty(local_decls, tcx);
221                        rap_debug!("place {:?}", place);
222                        match place_ty.ty.kind() {
223                            TyKind::Bool => {
224                                rap_debug!("SwitchInt via Bool");
225                                if let Some(constant) = self.mop_graph.constants.get(&place) {
226                                    if *constant != usize::MAX {
227                                        single_target = true;
228                                        sw_val = *constant;
229                                    }
230                                }
231                                path_discr_id = place;
232                                sw_targets = Some(targets.clone());
233                            }
234                            _ => {
235                                if let Some(father) = self
236                                    .mop_graph
237                                    .discriminants
238                                    .get(&self.mop_graph.values[place].local)
239                                {
240                                    if let Some(constant) = self.mop_graph.constants.get(father) {
241                                        if *constant != usize::MAX {
242                                            single_target = true;
243                                            sw_val = *constant;
244                                        }
245                                    }
246                                    if self.mop_graph.values[place].local == place {
247                                        path_discr_id = *father;
248                                        sw_targets = Some(targets.clone());
249                                    }
250                                }
251                            }
252                        }
253                    }
254                    Constant(c) => {
255                        single_target = true;
256                        let ty_env =
257                            TypingEnv::post_analysis(self.mop_graph.tcx, self.mop_graph.def_id);
258                        if let Some(val) =
259                            c.const_.try_eval_target_usize(self.mop_graph.tcx, ty_env)
260                        {
261                            sw_val = val as usize;
262                        }
263                    }
264                }
265                if single_target {
266                    /* Find the target based on the value;
267                     * Since sw_val is a const, only one target is reachable.
268                     * Filed 0 is the value; field 1 is the real target.
269                     */
270                    for iter in targets.iter() {
271                        if iter.0 as usize == sw_val {
272                            sw_target = iter.1.as_usize();
273                            break;
274                        }
275                    }
276                    /* No target found, choose the default target.
277                     * The default targets is not included within the iterator.
278                     * We can only obtain the default target based on the last item of all_targets().
279                     */
280                    if sw_target == 0 {
281                        let all_target = targets.all_targets();
282                        sw_target = all_target[all_target.len() - 1].as_usize();
283                    }
284                }
285            }
286        }
287        /* End: finish handling SwitchInt */
288        // fixed path since a constant switchInt value
289        if single_target {
290            self.check(sw_target, tcx, fn_map);
291        } else {
292            // Other cases in switchInt terminators
293            if let Some(targets) = sw_targets {
294                for iter in targets.iter() {
295                    if self.mop_graph.visit_times > VISIT_LIMIT {
296                        continue;
297                    }
298                    let next_idx = iter.1.as_usize();
299                    let path_discr_val = iter.0 as usize;
300                    self.split_check_with_cond(
301                        next_idx,
302                        path_discr_id,
303                        path_discr_val,
304                        tcx,
305                        fn_map,
306                    );
307                }
308                let all_targets = targets.all_targets();
309                let next_idx = all_targets[all_targets.len() - 1].as_usize();
310                let path_discr_val = usize::MAX; // to indicate the default path;
311                self.split_check_with_cond(next_idx, path_discr_id, path_discr_val, tcx, fn_map);
312            } else {
313                for i in &cur_block.next {
314                    if self.mop_graph.visit_times > VISIT_LIMIT {
315                        continue;
316                    }
317                    let next_idx = i;
318                    self.split_check(*next_idx, tcx, fn_map);
319                }
320            }
321        }
322        rap_debug!("Values: {:?}", self.mop_graph.values);
323        rap_debug!("Alias: {:?}", self.mop_graph.alias_set);
324    }
325}