rapx/analysis/core/alias_analysis/default/
mop.rs

1use super::{graph::*, *};
2use rustc_data_structures::fx::FxHashSet;
3use rustc_hir::def_id::DefId;
4use rustc_middle::{
5    mir::{
6        Operand::{Constant, Copy, Move},
7        TerminatorKind,
8    },
9    ty::TypingEnv,
10};
11
12use std::{
13    collections::{HashMap, HashSet},
14    env,
15};
16
17impl<'tcx> MopGraph<'tcx> {
18    pub fn split_check(
19        &mut self,
20        bb_index: usize,
21        fn_map: &mut MopAAResultMap,
22        recursion_set: &mut HashSet<DefId>,
23    ) {
24        /* duplicate the status before visiting a path; */
25        let backup_values = self.values.clone(); // duplicate the status when visiting different paths;
26        let backup_constant = self.constant.clone();
27        let backup_alias_set = self.alias_set.clone();
28        self.check(bb_index, fn_map, recursion_set);
29        /* restore after visit */
30        self.alias_set = backup_alias_set;
31        self.values = backup_values;
32        self.constant = backup_constant;
33    }
34    pub fn split_check_with_cond(
35        &mut self,
36        bb_index: usize,
37        path_discr_id: usize,
38        path_discr_val: usize,
39        fn_map: &mut MopAAResultMap,
40        recursion_set: &mut HashSet<DefId>,
41    ) {
42        /* duplicate the status before visiting a path; */
43        let backup_values = self.values.clone(); // duplicate the status when visiting different paths;
44        let backup_constant = self.constant.clone();
45        let backup_alias_set = self.alias_set.clone();
46        /* add control-sensitive indicator to the path status */
47        self.constant.insert(path_discr_id, path_discr_val);
48        self.check(bb_index, fn_map, recursion_set);
49        /* restore after visit */
50        self.alias_set = backup_alias_set;
51        self.values = backup_values;
52        self.constant = backup_constant;
53    }
54
55    // the core function of the safedrop.
56    pub fn check(
57        &mut self,
58        bb_index: usize,
59        fn_map: &mut MopAAResultMap,
60        recursion_set: &mut HashSet<DefId>,
61    ) {
62        self.visit_times += 1;
63        if self.visit_times > VISIT_LIMIT {
64            return;
65        }
66        let cur_block = self.blocks[self.scc_indices[bb_index]].clone();
67        self.alias_bb(self.scc_indices[bb_index]);
68        self.alias_bbcall(self.scc_indices[bb_index], fn_map, recursion_set);
69
70        if self.child_scc.get(&self.scc_indices[bb_index]).is_some() {
71            let init_index = self.scc_indices[bb_index];
72            let (init_block, cur_targets, scc_block_set) =
73                self.child_scc.get(&init_index).unwrap().clone();
74
75            for enum_index in cur_targets.all_targets() {
76                let backup_values = self.values.clone();
77                let backup_constant = self.constant.clone();
78
79                let mut block_node = if bb_index == init_index {
80                    init_block.clone()
81                } else {
82                    self.blocks[bb_index].clone()
83                };
84
85                if !block_node.switch_stmts.is_empty() {
86                    let TerminatorKind::SwitchInt { discr: _, targets } =
87                        block_node.switch_stmts[0].kind.clone()
88                    else {
89                        unreachable!();
90                    };
91                    if cur_targets == targets {
92                        block_node.next = FxHashSet::default();
93                        block_node.next.insert(enum_index.index());
94                    }
95                }
96
97                let mut work_list = Vec::new();
98                let mut work_set = FxHashSet::<usize>::default();
99                work_list.push(bb_index);
100                work_set.insert(bb_index);
101                while !work_list.is_empty() {
102                    let current_node = work_list.pop().unwrap();
103                    block_node.scc_sub_blocks.push(current_node);
104                    let real_node = if current_node != init_index {
105                        self.blocks[current_node].clone()
106                    } else {
107                        init_block.clone()
108                    };
109
110                    if real_node.switch_stmts.is_empty() {
111                        for next in &real_node.next {
112                            block_node.next.insert(*next);
113                        }
114                    } else {
115                        let TerminatorKind::SwitchInt {
116                            discr: _,
117                            ref targets,
118                        } = real_node.switch_stmts[0].kind
119                        else {
120                            unreachable!();
121                        };
122
123                        if cur_targets == *targets {
124                            block_node.next.insert(enum_index.index());
125                        } else {
126                            for next in &real_node.next {
127                                block_node.next.insert(*next);
128                            }
129                        }
130                    }
131
132                    if real_node.switch_stmts.is_empty() {
133                        for next in &real_node.next {
134                            if scc_block_set.contains(next) && !work_set.contains(next) {
135                                work_set.insert(*next);
136                                work_list.push(*next);
137                            }
138                        }
139                    } else {
140                        let TerminatorKind::SwitchInt {
141                            discr: _,
142                            ref targets,
143                        } = real_node.switch_stmts[0].kind
144                        else {
145                            unreachable!();
146                        };
147
148                        if cur_targets == *targets {
149                            let next_index = enum_index.index();
150                            if scc_block_set.contains(&next_index)
151                                && !work_set.contains(&next_index)
152                            {
153                                work_set.insert(next_index);
154                                work_list.push(next_index);
155                            }
156                        } else {
157                            for next in &real_node.next {
158                                if scc_block_set.contains(next) && !work_set.contains(next) {
159                                    work_set.insert(*next);
160                                    work_list.push(*next);
161                                }
162                            }
163                        }
164                    }
165                }
166
167                /* remove next nodes which are already in the current SCC */
168                let mut to_remove = Vec::new();
169                for i in block_node.next.iter() {
170                    if self.scc_indices[*i] == init_index {
171                        to_remove.push(*i);
172                    }
173                }
174                for i in to_remove {
175                    block_node.next.remove(&i);
176                }
177
178                for i in block_node.scc_sub_blocks.clone() {
179                    self.alias_bb(i);
180                    self.alias_bbcall(i, fn_map, recursion_set);
181                }
182                /* Reach a leaf node, check bugs */
183                match block_node.next.len() {
184                    0 => {}
185                    _ => {
186                        /*
187                         * Equivalent to self.check(cur_block.next[0]..);
188                         * We cannot use [0] for FxHashSet.
189                         */
190                        for next in block_node.next {
191                            self.check(next, fn_map, recursion_set);
192                        }
193                    }
194                }
195
196                self.values = backup_values;
197                self.constant = backup_constant;
198            }
199
200            return;
201        }
202
203        let mut order = vec![];
204        order.push(vec![]);
205
206        /* Handle cases if the current block is a merged scc block with sub block */
207        if !cur_block.scc_sub_blocks.is_empty() {
208            match env::var_os("MOP") {
209                Some(val) if val == "0" => {
210                    order.push(cur_block.scc_sub_blocks.clone());
211                }
212                _ => {
213                    self.calculate_scc_order(
214                        &mut cur_block.scc_sub_blocks.clone(),
215                        &mut vec![],
216                        &mut order,
217                        &mut HashMap::new(),
218                        bb_index,
219                        bb_index,
220                        &mut HashSet::new(),
221                    );
222                }
223            }
224        }
225
226        let backup_values = self.values.clone(); // duplicate the status when visiting different paths;
227        let backup_constant = self.constant.clone();
228        let backup_alias_set = self.alias_set.clone();
229        let backup_fn_map = fn_map.clone();
230        let backup_recursion_set = recursion_set.clone();
231        for scc_each in order {
232            self.alias_set = backup_alias_set.clone();
233            self.values = backup_values.clone();
234            self.constant = backup_constant.clone();
235            *fn_map = backup_fn_map.clone();
236            *recursion_set = backup_recursion_set.clone();
237
238            if !scc_each.is_empty() {
239                for idx in scc_each {
240                    self.alias_bb(idx);
241                    self.alias_bbcall(idx, fn_map, recursion_set);
242                }
243            }
244
245            let cur_block = cur_block.clone();
246            /* Reach a leaf node, check bugs */
247            match cur_block.next.len() {
248                0 => {
249                    let results_nodes = self.values.clone();
250                    self.merge_results(results_nodes);
251                    return;
252                }
253                1 => {
254                    /*
255                     * Equivalent to self.check(cur_block.next[0]..);
256                     * We cannot use [0] for FxHashSet.
257                     */
258                    for next in cur_block.next {
259                        self.check(next, fn_map, recursion_set);
260                    }
261                    return;
262                }
263                _ => { // multiple blocks
264                }
265            }
266
267            /* Begin: handle the SwitchInt statement. */
268            let mut single_target = false;
269            let mut sw_val = 0;
270            let mut sw_target = 0; // Single target
271            let mut path_discr_id = 0; // To avoid analyzing paths that cannot be reached with one enum type.
272            let mut sw_targets = None; // Multiple targets of SwitchInt
273            if !cur_block.switch_stmts.is_empty() && cur_block.scc_sub_blocks.is_empty() {
274                if let TerminatorKind::SwitchInt {
275                    ref discr,
276                    ref targets,
277                } = cur_block.switch_stmts[0].clone().kind
278                {
279                    match discr {
280                        Copy(p) | Move(p) => {
281                            let place = self.projection(false, *p);
282                            if let Some(father) = self.disc_map.get(&self.values[place].local) {
283                                if let Some(constant) = self.constant.get(father) {
284                                    if *constant != usize::MAX {
285                                        single_target = true;
286                                        sw_val = *constant;
287                                    }
288                                }
289                                if self.values[place].local == place {
290                                    path_discr_id = *father;
291                                    sw_targets = Some(targets.clone());
292                                }
293                            }
294                        }
295                        Constant(c) => {
296                            single_target = true;
297                            let ty_env = TypingEnv::post_analysis(self.tcx, self.def_id);
298                            if let Some(val) = c.const_.try_eval_target_usize(self.tcx, ty_env) {
299                                sw_val = val as usize;
300                            }
301                        }
302                    }
303                    if single_target {
304                        /* Find the target based on the value;
305                         * Since sw_val is a const, only one target is reachable.
306                         * Filed 0 is the value; field 1 is the real target.
307                         */
308                        for iter in targets.iter() {
309                            if iter.0 as usize == sw_val {
310                                sw_target = iter.1.as_usize();
311                                break;
312                            }
313                        }
314                        /* No target found, choose the default target.
315                         * The default targets is not included within the iterator.
316                         * We can only obtain the default target based on the last item of all_targets().
317                         */
318                        if sw_target == 0 {
319                            let all_target = targets.all_targets();
320                            sw_target = all_target[all_target.len() - 1].as_usize();
321                        }
322                    }
323                }
324            }
325            /* End: finish handling SwitchInt */
326            // fixed path since a constant switchInt value
327            if single_target {
328                self.check(sw_target, fn_map, recursion_set);
329            } else {
330                // Other cases in switchInt terminators
331                if let Some(targets) = sw_targets {
332                    for iter in targets.iter() {
333                        if self.visit_times > VISIT_LIMIT {
334                            continue;
335                        }
336                        let next_index = iter.1.as_usize();
337                        let path_discr_val = iter.0 as usize;
338                        self.split_check_with_cond(
339                            next_index,
340                            path_discr_id,
341                            path_discr_val,
342                            fn_map,
343                            recursion_set,
344                        );
345                    }
346                    let all_targets = targets.all_targets();
347                    let next_index = all_targets[all_targets.len() - 1].as_usize();
348                    let path_discr_val = usize::MAX; // to indicate the default path;
349                    self.split_check_with_cond(
350                        next_index,
351                        path_discr_id,
352                        path_discr_val,
353                        fn_map,
354                        recursion_set,
355                    );
356                } else {
357                    for i in cur_block.next {
358                        if self.visit_times > VISIT_LIMIT {
359                            continue;
360                        }
361                        let next_index = i;
362                        self.split_check(next_index, fn_map, recursion_set);
363                    }
364                }
365            }
366        }
367    }
368
369    pub fn calculate_scc_order(
370        &mut self,
371        scc: &Vec<usize>,
372        path: &mut Vec<usize>,
373        ans: &mut Vec<Vec<usize>>,
374        disc_map: &mut HashMap<usize, usize>,
375        idx: usize,
376        root: usize,
377        visit: &mut HashSet<usize>,
378    ) {
379        if idx == root && !path.is_empty() {
380            ans.push(path.clone());
381            return;
382        }
383        visit.insert(idx);
384        let term = &self.terms[idx].clone();
385
386        match term {
387            TerminatorKind::SwitchInt {
388                ref discr,
389                ref targets,
390            } => match discr {
391                Copy(p) | Move(p) => {
392                    let place = self.projection(false, *p);
393                    if let Some(father) = self.disc_map.get(&self.values[place].local) {
394                        let father = *father;
395                        if let Some(constant) = disc_map.get(&father) {
396                            let constant = *constant;
397                            for t in targets.iter() {
398                                if t.0 as usize == constant {
399                                    let target = t.1.as_usize();
400                                    if path.contains(&target) {
401                                        continue;
402                                    }
403                                    path.push(target);
404                                    self.calculate_scc_order(
405                                        scc, path, ans, disc_map, target, root, visit,
406                                    );
407                                    path.pop();
408                                }
409                            }
410                        } else {
411                            for t in targets.iter() {
412                                let constant = t.0 as usize;
413                                let target = t.1.as_usize();
414                                if path.contains(&target) {
415                                    continue;
416                                }
417                                path.push(target);
418                                disc_map.insert(father, constant);
419                                self.calculate_scc_order(
420                                    scc, path, ans, disc_map, target, root, visit,
421                                );
422                                disc_map.remove(&father);
423                                path.pop();
424                            }
425                        }
426                    } else {
427                        if let Some(constant) = disc_map.get(&place) {
428                            let constant = *constant;
429                            for t in targets.iter() {
430                                if t.0 as usize == constant {
431                                    let target = t.1.as_usize();
432                                    if path.contains(&target) {
433                                        continue;
434                                    }
435                                    path.push(target);
436                                    self.calculate_scc_order(
437                                        scc, path, ans, disc_map, target, root, visit,
438                                    );
439                                    path.pop();
440                                }
441                            }
442                        } else {
443                            for t in targets.iter() {
444                                let constant = t.0 as usize;
445                                let target = t.1.as_usize();
446                                if path.contains(&target) {
447                                    continue;
448                                }
449                                path.push(target);
450                                disc_map.insert(place, constant);
451                                self.calculate_scc_order(
452                                    scc, path, ans, disc_map, target, root, visit,
453                                );
454                                disc_map.remove(&place);
455                                path.pop();
456                            }
457
458                            let constant = targets.iter().len();
459                            let target = targets.otherwise().as_usize();
460                            if !path.contains(&target) {
461                                path.push(target);
462                                disc_map.insert(place, constant);
463                                self.calculate_scc_order(
464                                    scc, path, ans, disc_map, target, root, visit,
465                                );
466                                disc_map.remove(&place);
467                                path.pop();
468                            }
469                        }
470                    }
471                }
472                _ => {}
473            },
474            _ => {
475                for bidx in self.blocks[idx].next.clone() {
476                    if !scc.contains(&bidx) && bidx != root {
477                        continue;
478                    }
479                    if bidx != root {
480                        path.push(bidx);
481                        self.calculate_scc_order(scc, path, ans, disc_map, bidx, root, visit);
482                        path.pop();
483                    } else {
484                        self.calculate_scc_order(scc, path, ans, disc_map, bidx, root, visit);
485                    }
486                }
487            }
488        }
489
490        visit.remove(&idx);
491    }
492}