rapx/analysis/senryx/
visitor.rs

1use crate::{
2    analysis::{
3        core::{
4            alias_analysis::AAResult,
5            ownedheap_analysis::OHAResultMap,
6            range_analysis::{default::RangeAnalyzer, RangeAnalysis},
7        },
8        safedrop::graph::SafeDropGraph,
9        senryx::contracts::property::{CisRangeItem, PropertyContract},
10        utils::{
11            fn_info::{
12                display_hashmap, get_all_std_unsafe_callees_block_id, get_callees,
13                get_cleaned_def_path_name, is_ptr, is_ref,
14            },
15            show_mir::display_mir,
16        },
17        Analysis,
18    },
19    rap_debug, rap_warn,
20};
21use rustc_middle::ty::GenericParamDefKind;
22use serde::de;
23use std::{
24    collections::{HashMap, HashSet},
25    fmt::Debug,
26    hash::Hash,
27};
28use syn::Constraint;
29
30use super::contracts::abstract_state::{
31    AbstractStateItem, AlignState, PathInfo, StateType, VType, Value,
32};
33use super::contracts::contract::Contract;
34use super::dominated_graph::DominatedGraph;
35use super::dominated_graph::InterResultNode;
36use super::generic_check::GenericChecker;
37use super::inter_record::InterAnalysisRecord;
38use super::matcher::UnsafeApi;
39use super::matcher::{get_arg_place, parse_unsafe_api};
40use rustc_data_structures::fx::FxHashMap;
41use rustc_hir::def_id::DefId;
42use rustc_middle::{
43    mir::{
44        self, AggregateKind, BasicBlock, BasicBlockData, BinOp, CastKind, Local, Operand, Place,
45        ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
46    },
47    ty::{self, GenericArgKind, PseudoCanonicalInput, Ty, TyCtxt, TyKind},
48};
49use rustc_span::{source_map::Spanned, Span};
50
51//TODO: modify contracts vec to contract-bool pairs (we can also use path index to record path info)
52pub struct CheckResult {
53    pub func_name: String,
54    pub func_span: Span,
55    pub failed_contracts: HashMap<usize, HashSet<String>>,
56    pub passed_contracts: HashMap<usize, HashSet<String>>,
57}
58
59impl CheckResult {
60    pub fn new(func_name: &str, func_span: Span) -> Self {
61        Self {
62            func_name: func_name.to_string(),
63            func_span,
64            failed_contracts: HashMap::new(),
65            passed_contracts: HashMap::new(),
66        }
67    }
68}
69
70#[derive(Debug, PartialEq, Eq, Clone)]
71pub enum PlaceTy<'tcx> {
72    Ty(usize, usize), // layout(align,size) of one specific type
73    GenericTy(String, HashSet<Ty<'tcx>>, HashSet<(usize, usize)>), // get specific type in generic map
74    Unknown,
75}
76
77impl<'tcx> PlaceTy<'tcx> {
78    pub fn possible_aligns(&self) -> HashSet<usize> {
79        match self {
80            PlaceTy::Ty(align, _size) => {
81                let mut set = HashSet::new();
82                set.insert(*align);
83                set
84            }
85            PlaceTy::GenericTy(_, _, tys) => tys.iter().map(|ty| ty.0).collect(),
86            _ => HashSet::new(),
87        }
88    }
89}
90
91impl<'tcx> Hash for PlaceTy<'tcx> {
92    fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {}
93}
94
95pub struct BodyVisitor<'tcx> {
96    pub tcx: TyCtxt<'tcx>,
97    pub def_id: DefId,
98    pub safedrop_graph: SafeDropGraph<'tcx>,
99    // abstract_states records the path index and variables' ab states in this path
100    pub abstract_states: HashMap<usize, PathInfo<'tcx>>,
101    pub unsafe_callee_report: HashMap<String, usize>,
102    pub local_ty: HashMap<usize, PlaceTy<'tcx>>,
103    pub visit_time: usize,
104    pub check_results: Vec<CheckResult>,
105    pub generic_map: HashMap<String, HashSet<Ty<'tcx>>>,
106    pub global_recorder: HashMap<DefId, InterAnalysisRecord<'tcx>>,
107    pub proj_ty: HashMap<usize, Ty<'tcx>>,
108    pub chains: DominatedGraph<'tcx>,
109    // pub paths: HashSet<Vec<usize>, (Place<'tcx>, Place<'tcx>, BinOp)>,
110}
111
112impl<'tcx> BodyVisitor<'tcx> {
113    pub fn new(
114        tcx: TyCtxt<'tcx>,
115        def_id: DefId,
116        global_recorder: HashMap<DefId, InterAnalysisRecord<'tcx>>,
117        visit_time: usize,
118    ) -> Self {
119        let body = tcx.optimized_mir(def_id);
120        let param_env = tcx.param_env(def_id);
121        let satisfied_ty_map_for_generic =
122            GenericChecker::new(tcx, param_env).get_satisfied_ty_map();
123        let mut chains = DominatedGraph::new(tcx, def_id);
124        chains.init_arg();
125        Self {
126            tcx,
127            def_id,
128            safedrop_graph: SafeDropGraph::new(body, tcx, def_id, OHAResultMap::default()),
129            abstract_states: HashMap::new(),
130            unsafe_callee_report: HashMap::new(),
131            local_ty: HashMap::new(),
132            visit_time,
133            check_results: Vec::new(),
134            generic_map: satisfied_ty_map_for_generic,
135            global_recorder,
136            proj_ty: HashMap::new(),
137            chains,
138            // paths: HashSet::new(),
139        }
140    }
141
142    pub fn get_ty_by_place(&self, p: usize) -> Ty<'tcx> {
143        let body = self.tcx.optimized_mir(self.def_id);
144        let locals = body.local_decls.clone();
145        return locals[Local::from(p)].ty;
146    }
147
148    pub fn update_fields_states(&mut self, inter_result: InterResultNode<'tcx>) {
149        self.chains.init_self_with_inter(inter_result);
150    }
151
152    pub fn path_forward_check(
153        &mut self,
154        fn_map: &FxHashMap<DefId, AAResult>,
155    ) -> InterResultNode<'tcx> {
156        let mut inter_return_value =
157            InterResultNode::construct_from_var_node(self.chains.clone(), 0);
158        if self.visit_time >= 1000 {
159            return inter_return_value;
160        }
161
162        // get path and body
163        let paths = self.get_all_paths();
164        // self.paths = paths.clone();
165        let body = self.tcx.optimized_mir(self.def_id);
166
167        // initialize local vars' types
168        let locals = body.local_decls.clone();
169        for (idx, local) in locals.iter().enumerate() {
170            let local_ty = local.ty;
171            let layout = self.visit_ty_and_get_layout(local_ty);
172            self.local_ty.insert(idx, layout);
173        }
174
175        // Iterate all the paths. Paths have been handled by tarjan.
176        let tmp_chain = self.chains.clone();
177        for (index, (path, constraint)) in paths.iter().enumerate() {
178            self.chains = tmp_chain.clone();
179            self.set_constraint(constraint);
180            self.abstract_states.insert(index, PathInfo::new());
181            for block_index in path.iter() {
182                if block_index >= &body.basic_blocks.len() {
183                    continue;
184                }
185                self.path_analyze_block(
186                    &body.basic_blocks[BasicBlock::from_usize(*block_index)].clone(),
187                    index,
188                    *block_index,
189                    fn_map,
190                );
191                let tem_scc_sub_blocks = self.safedrop_graph.blocks[*block_index]
192                    .scc_sub_blocks
193                    .clone();
194                if tem_scc_sub_blocks.len() > 0 {
195                    for sub_block in &tem_scc_sub_blocks {
196                        self.path_analyze_block(
197                            &body.basic_blocks[BasicBlock::from_usize(*sub_block)].clone(),
198                            index,
199                            *block_index,
200                            fn_map,
201                        );
202                    }
203                }
204            }
205            // merge path analysis results
206            let curr_path_inter_return_value =
207                InterResultNode::construct_from_var_node(self.chains.clone(), 0);
208            inter_return_value.merge(curr_path_inter_return_value);
209        }
210
211        inter_return_value
212    }
213
214    pub fn path_analyze_block(
215        &mut self,
216        block: &BasicBlockData<'tcx>,
217        path_index: usize,
218        bb_index: usize,
219        fn_map: &FxHashMap<DefId, AAResult>,
220    ) {
221        for statement in block.statements.iter() {
222            self.path_analyze_statement(statement, path_index);
223        }
224        self.path_analyze_terminator(&block.terminator(), path_index, bb_index, fn_map);
225    }
226
227    pub fn path_analyze_statement(&mut self, statement: &Statement<'tcx>, _path_index: usize) {
228        match statement.kind {
229            StatementKind::Assign(box (ref lplace, ref rvalue)) => {
230                self.path_analyze_assign(lplace, rvalue, _path_index);
231            }
232            StatementKind::Intrinsic(box ref intrinsic) => match intrinsic {
233                mir::NonDivergingIntrinsic::CopyNonOverlapping(cno) => {
234                    if cno.src.place().is_some() && cno.dst.place().is_some() {
235                        let _src_pjc_local =
236                            self.handle_proj(true, cno.src.place().unwrap().clone());
237                        let _dst_pjc_local =
238                            self.handle_proj(true, cno.dst.place().unwrap().clone());
239                    }
240                }
241                _ => {}
242            },
243            StatementKind::StorageDead(local) => {
244                // self.chains.delete_node(local.as_usize());
245            }
246            _ => {}
247        }
248    }
249
250    pub fn path_analyze_terminator(
251        &mut self,
252        terminator: &Terminator<'tcx>,
253        path_index: usize,
254        bb_index: usize,
255        fn_map: &FxHashMap<DefId, AAResult>,
256    ) {
257        match &terminator.kind {
258            TerminatorKind::Call {
259                func,
260                args,
261                destination: dst_place,
262                target: _,
263                unwind: _,
264                call_source: _,
265                fn_span,
266            } => {
267                if let Operand::Constant(func_constant) = func {
268                    if let ty::FnDef(ref callee_def_id, raw_list) = func_constant.const_.ty().kind()
269                    {
270                        let mut mapping = FxHashMap::default();
271                        self.get_generic_mapping(raw_list.as_slice(), callee_def_id, &mut mapping);
272                        rap_debug!(
273                            "func {:?}, generic type mapping {:?}",
274                            callee_def_id,
275                            mapping
276                        );
277                        self.handle_call(
278                            dst_place,
279                            callee_def_id,
280                            args,
281                            path_index,
282                            fn_map,
283                            *fn_span,
284                            mapping,
285                        );
286                    }
287                }
288            }
289            TerminatorKind::Drop {
290                place,
291                target: _,
292                unwind: _,
293                replace: _,
294                drop: _,
295                async_fut: _,
296            } => {
297                let drop_local = self.handle_proj(false, *place);
298                if !self.chains.set_drop(drop_local) {
299                    // rap_warn!(
300                    //     "In path {:?}, double drop {drop_local} in block {bb_index}",
301                    //     self.paths[path_index]
302                    // );
303                }
304            }
305            _ => {}
306        }
307    }
308
309    /// Get the generic name to an actual type mapping when used for a def_id.
310    /// If current def_id doesn't have generic, then search its parent.
311    /// The generic set include type and allocator.
312    /// Example: generic_mapping (T -> u32, A -> std::alloc::Global)
313    fn get_generic_mapping(
314        &self,
315        raw_list: &[rustc_middle::ty::GenericArg<'tcx>],
316        def_id: &DefId,
317        generic_mapping: &mut FxHashMap<String, Ty<'tcx>>,
318    ) {
319        let generics = self.tcx.generics_of(def_id);
320        for param in &generics.own_params {
321            if let GenericParamDefKind::Type {
322                has_default: _,
323                synthetic: _,
324            } = param.kind
325            {
326                if let Some(ty) = raw_list.get(param.index as usize) {
327                    if let GenericArgKind::Type(actual_ty) = (*ty).kind() {
328                        let param_name = param.name.to_string();
329                        generic_mapping.insert(param_name, actual_ty);
330                    }
331                }
332            }
333        }
334        if generics.own_params.len() == 0 && generics.parent.is_some() {
335            let parent_def_id = generics.parent.unwrap();
336            self.get_generic_mapping(raw_list, &parent_def_id, generic_mapping);
337        }
338    }
339
340    pub fn path_analyze_assign(
341        &mut self,
342        lplace: &Place<'tcx>,
343        rvalue: &Rvalue<'tcx>,
344        path_index: usize,
345    ) {
346        let lpjc_local = self.handle_proj(false, lplace.clone());
347        match rvalue {
348            Rvalue::Use(op) => match op {
349                Operand::Move(rplace) => {
350                    let rpjc_local = self.handle_proj(true, rplace.clone());
351                    self.chains.merge(lpjc_local, rpjc_local);
352                }
353                Operand::Copy(rplace) => {
354                    let rpjc_local = self.handle_proj(true, rplace.clone());
355                    self.chains.copy_node(lpjc_local, rpjc_local);
356                }
357                _ => {}
358            },
359            Rvalue::Repeat(op, _const) => match op {
360                Operand::Move(rplace) | Operand::Copy(rplace) => {
361                    let _rpjc_local = self.handle_proj(true, rplace.clone());
362                }
363                _ => {}
364            },
365            Rvalue::Ref(_, _, rplace) | Rvalue::RawPtr(_, rplace) => {
366                let rpjc_local = self.handle_proj(true, rplace.clone());
367                // self.chains.insert_node(lpjc_local, self.chains.get_local_ty_by_place(lpjc_local));
368                self.chains.point(lpjc_local, rpjc_local);
369            }
370            Rvalue::Cast(cast_kind, op, ty) => match op {
371                Operand::Move(rplace) | Operand::Copy(rplace) => {
372                    let rpjc_local = self.handle_proj(true, rplace.clone());
373                    let r_point_to = self.chains.get_point_to_id(rpjc_local);
374                    if r_point_to == rpjc_local {
375                        self.chains.merge(lpjc_local, rpjc_local);
376                    } else {
377                        self.chains.point(lpjc_local, r_point_to);
378                    }
379                }
380                _ => {}
381            },
382            Rvalue::BinaryOp(_bin_op, box (ref _op1, ref _op2)) => {}
383            Rvalue::ShallowInitBox(op, _ty) => match op {
384                Operand::Move(rplace) | Operand::Copy(rplace) => {
385                    let _rpjc_local = self.handle_proj(true, rplace.clone());
386                }
387                _ => {}
388            },
389            Rvalue::Aggregate(box ref agg_kind, op_vec) => match agg_kind {
390                AggregateKind::Array(_ty) => {}
391                AggregateKind::Adt(_adt_def_id, _, _, _, _) => {
392                    for (idx, op) in op_vec.into_iter().enumerate() {
393                        let (is_const, val) = get_arg_place(op);
394                        if is_const {
395                            self.chains.insert_field_node(
396                                lpjc_local,
397                                idx,
398                                Some(Ty::new_uint(self.tcx, rustc_middle::ty::UintTy::Usize)),
399                            );
400                        } else {
401                            let node = self.chains.get_var_node_mut(lpjc_local).unwrap();
402                            node.field.insert(idx, val);
403                        }
404                    }
405                }
406                _ => {}
407            },
408            Rvalue::Discriminant(_place) => {
409                // println!("Discriminant {}:{:?}",lpjc_local,rvalue);
410            }
411            _ => {}
412        }
413    }
414
415    pub fn handle_call(
416        &mut self,
417        dst_place: &Place<'tcx>,
418        def_id: &DefId,
419        args: &Box<[Spanned<Operand>]>,
420        path_index: usize,
421        fn_map: &FxHashMap<DefId, AAResult>,
422        fn_span: Span,
423        generic_mapping: FxHashMap<String, Ty<'tcx>>,
424    ) {
425        if !self.tcx.is_mir_available(def_id) {
426            self.insert_path_abstate(
427                path_index,
428                dst_place.local.as_usize(),
429                AbstractStateItem::new_default(),
430            );
431            return;
432        }
433
434        // Find std unsafe API call, then check the contracts.
435        if let Some(fn_result) =
436            parse_unsafe_api(get_cleaned_def_path_name(self.tcx, *def_id).as_str())
437        {
438            self.handle_std_unsafe_call(
439                dst_place,
440                def_id,
441                args,
442                path_index,
443                fn_map,
444                fn_span,
445                fn_result,
446                generic_mapping,
447            );
448        }
449
450        self.set_bound(def_id, dst_place, args);
451
452        // merge alias results
453        self.handle_ret_alias(dst_place, def_id, fn_map, args);
454
455        // TODO: to be deleted!
456        // get pre analysis state
457        // let mut pre_analysis_state = HashMap::new();
458        // for (idx, arg) in args.iter().enumerate() {
459        //     let arg_place = get_arg_place(&arg.node);
460        //     let ab_state_item = self.get_abstate_by_place_in_path(arg_place.1, path_index);
461        //     pre_analysis_state.insert(idx, ab_state_item);
462        // }
463
464        // // check cache and update new states for args and return value
465        // let mut gr = self.global_recorder.clone();
466        // if let Some(record) = gr.get_mut(def_id) {
467        //     if record.is_pre_state_same(&pre_analysis_state) {
468        //         self.update_post_state(&record.post_analysis_state, args, path_index);
469        //         self.insert_path_abstate(
470        //             path_index,
471        //             dst_place.local.as_usize(),
472        //             record.ret_state.clone(),
473        //         );
474        //         return;
475        //     }
476        // }
477
478        // update post states and cache
479        // let tcx = self.tcx;
480        // let mut inter_body_visitor: BodyVisitor<'_> = BodyVisitor::new(
481        //     tcx,
482        //     *def_id,
483        //     self.global_recorder.clone(),
484        //     self.visit_time + 1,
485        // );
486        // inter_body_visitor.path_forward_check(fn_map);
487        // let post_analysis_state: HashMap<usize, AbstractStateItem<'_>> =
488        //     inter_body_visitor.get_args_post_states().clone();
489        // self.update_post_state(&post_analysis_state, args, path_index);
490        // let ret_state = post_analysis_state.get(&0).unwrap().clone();
491        // self.global_recorder.insert(
492        //     *def_id,
493        //     InterAnalysisRecord::new(pre_analysis_state, post_analysis_state, ret_state),
494        // );
495    }
496
497    fn set_bound(
498        &mut self,
499        def_id: &DefId,
500        dst_place: &Place<'tcx>,
501        args: &Box<[Spanned<Operand>]>,
502    ) {
503        if args.len() == 0 || !get_cleaned_def_path_name(self.tcx, *def_id).contains("slice::len") {
504            return;
505        }
506        let d_local = self.handle_proj(false, dst_place.clone());
507        let ptr_local = get_arg_place(&args[0].node).1;
508        let mem_local = self.chains.get_point_to_id(ptr_local);
509        let mem_var = self.chains.get_var_node_mut(mem_local).unwrap();
510        for cis in &mut mem_var.cis.contracts {
511            if let PropertyContract::InBound(cis_ty, len) = cis {
512                *len = CisRangeItem::new_var(d_local);
513            }
514        }
515    }
516
517    // Use the alias analysis to support quick merge inter analysis results.
518    pub fn handle_ret_alias(
519        &mut self,
520        dst_place: &Place<'tcx>,
521        def_id: &DefId,
522        fn_map: &FxHashMap<DefId, AAResult>,
523        args: &Box<[Spanned<Operand>]>,
524    ) {
525        let d_local = self.handle_proj(false, dst_place.clone());
526        // Find alias relationship in cache.
527        // If one of the op is ptr, then alias the pointed node with another.
528        if let Some(retalias) = fn_map.get(def_id) {
529            for alias_set in retalias.aliases() {
530                let (l, r) = (alias_set.lhs_no, alias_set.rhs_no);
531                let (l_fields, r_fields) =
532                    (alias_set.lhs_fields.clone(), alias_set.rhs_fields.clone());
533                let (l_place, r_place) = (
534                    if l != 0 {
535                        get_arg_place(&args[l - 1].node)
536                    } else {
537                        (false, d_local)
538                    },
539                    if r != 0 {
540                        get_arg_place(&args[r - 1].node)
541                    } else {
542                        (false, d_local)
543                    },
544                );
545                // if left value is a constant, then update right variable's value
546                if l_place.0 {
547                    let snd_var = self.chains.find_var_id_with_fields_seq(r_place.1, r_fields);
548                    self.chains
549                        .update_value(self.chains.get_point_to_id(snd_var), l_place.1);
550                    continue;
551                }
552                // if right value is a constant, then update left variable's value
553                if r_place.0 {
554                    let fst_var = self.chains.find_var_id_with_fields_seq(l_place.1, l_fields);
555                    self.chains
556                        .update_value(self.chains.get_point_to_id(fst_var), r_place.1);
557                    continue;
558                }
559                let (fst_var, snd_var) = (
560                    self.chains.find_var_id_with_fields_seq(l_place.1, l_fields),
561                    self.chains.find_var_id_with_fields_seq(r_place.1, r_fields),
562                );
563                // If this var is ptr or ref, then get the next level node.
564                let fst_to = self.chains.get_point_to_id(fst_var);
565                let snd_to = self.chains.get_point_to_id(snd_var);
566                let is_fst_point = fst_to != fst_var;
567                let is_snd_point = snd_to != snd_var;
568                let fst_node = self.chains.get_var_node(fst_var).unwrap();
569                let snd_node = self.chains.get_var_node(snd_var).unwrap();
570                let is_fst_ptr = is_ptr(fst_node.ty.unwrap()) || is_ref(fst_node.ty.unwrap());
571                let is_snd_ptr = is_ptr(snd_node.ty.unwrap()) || is_ref(snd_node.ty.unwrap());
572                rap_debug!(
573                    "{:?}: {fst_var},{fst_to},{is_fst_ptr} -- {snd_var},{snd_to},{is_snd_ptr}",
574                    def_id
575                );
576                match (is_fst_ptr, is_snd_ptr) {
577                    (false, true) => {
578                        // If this ptr didn't point to anywhere, then point to fst var
579                        if is_snd_point {
580                            self.chains.point(snd_var, fst_var);
581                        } else {
582                            self.chains.merge(fst_var, snd_to);
583                        }
584                    }
585                    (false, false) => {
586                        self.chains.merge(fst_var, snd_var);
587                    }
588                    (true, true) => {
589                        if is_fst_point && is_snd_point {
590                            self.chains.merge(fst_to, snd_to);
591                        } else if !is_fst_point && is_snd_point {
592                            self.chains.point(fst_var, snd_to);
593                        } else if is_fst_point && !is_snd_point {
594                            self.chains.point(snd_var, fst_to);
595                        } else {
596                            self.chains.merge(fst_var, snd_var);
597                        }
598                    }
599                    (true, false) => {
600                        if is_fst_point {
601                            self.chains.point(fst_var, snd_var);
602                        } else {
603                            self.chains.merge(snd_var, fst_to);
604                        }
605                    }
606                }
607            }
608        }
609        // If no alias cache is found and dst is a ptr, then initialize dst's states.
610        else {
611            let d_ty = self.chains.get_local_ty_by_place(d_local);
612            if d_ty.is_some() && (is_ptr(d_ty.unwrap()) || is_ref(d_ty.unwrap())) {
613                self.chains
614                    .generate_ptr_with_obj_node(d_ty.unwrap(), d_local);
615            }
616        }
617    }
618
619    // if inter analysis's params are in mut_ref, then we should update their post states
620    pub fn update_post_state(
621        &mut self,
622        post_state: &HashMap<usize, AbstractStateItem<'tcx>>,
623        args: &Box<[Spanned<Operand>]>,
624        path_index: usize,
625    ) {
626        for (idx, arg) in args.iter().enumerate() {
627            let arg_place = get_arg_place(&arg.node);
628            if let Some(state_item) = post_state.get(&idx) {
629                self.insert_path_abstate(path_index, arg_place.1, state_item.clone());
630            }
631        }
632    }
633
634    pub fn get_args_post_states(&mut self) -> HashMap<usize, AbstractStateItem<'tcx>> {
635        let tcx = self.tcx;
636        let def_id = self.def_id;
637        let final_states = self.abstract_states_mop();
638        let mut result_states = HashMap::new();
639        let fn_sig = tcx.fn_sig(def_id).skip_binder();
640        let num_params = fn_sig.inputs().skip_binder().len();
641        for i in 0..num_params + 1 {
642            if let Some(state) = final_states.state_map.get(&(i)) {
643                result_states.insert(i, state.clone());
644            } else {
645                result_states.insert(i, AbstractStateItem::new_default());
646            }
647        }
648        result_states
649    }
650
651    pub fn get_all_paths(&mut self) -> HashMap<Vec<usize>, Vec<(Place<'tcx>, Place<'tcx>, BinOp)>> {
652        let mut range_analyzer = RangeAnalyzer::<i128>::new(self.tcx, false);
653        let path_constraints_option =
654            range_analyzer.start_path_constraints_analysis_for_defid(self.def_id); // if def_id does not exist, this will break down
655        let mut path_constraints: HashMap<Vec<usize>, Vec<(_, _, _)>> =
656            if path_constraints_option.is_none() {
657                let mut results = HashMap::new();
658                let paths: Vec<Vec<usize>> = self.safedrop_graph.get_paths();
659                for path in paths {
660                    results.insert(path, Vec::new());
661                }
662                results
663            } else {
664                path_constraints_option.unwrap()
665            };
666        self.safedrop_graph.solve_scc();
667        // If it's the first level analysis, then filter the paths not containing unsafe
668        if self.visit_time == 0 {
669            let contains_unsafe_blocks = get_all_std_unsafe_callees_block_id(self.tcx, self.def_id);
670            path_constraints.retain(|path, cons| {
671                path.iter()
672                    .any(|block_id| contains_unsafe_blocks.contains(block_id))
673            });
674        }
675        // display_hashmap(&path_constraints, 1);
676        path_constraints
677    }
678
679    // pub fn get_all_paths(&mut self) -> Vec<Vec<usize>> {
680    //     self.safedrop_graph.solve_scc();
681    //     let mut results: Vec<Vec<usize>> = self.safedrop_graph.get_paths();
682    //     // If it's the first level analysis, then filter the paths not containing unsafe
683    //     if self.visit_time == 0 {
684    //         let contains_unsafe_blocks = get_all_std_unsafe_callees_block_id(self.tcx, self.def_id);
685    //         results.retain(|path| {
686    //             path.iter()
687    //                 .any(|block_id| contains_unsafe_blocks.contains(block_id))
688    //         });
689    //     }
690    //     results
691    // }
692
693    pub fn abstract_states_mop(&mut self) -> PathInfo<'tcx> {
694        let mut result_state = PathInfo {
695            state_map: HashMap::new(),
696        };
697
698        for (_path_idx, abstract_state) in &self.abstract_states {
699            for (var_index, state_item) in &abstract_state.state_map {
700                if let Some(existing_state_item) = result_state.state_map.get_mut(&var_index) {
701                    existing_state_item
702                        .clone()
703                        .meet_state_item(&state_item.clone());
704                } else {
705                    result_state
706                        .state_map
707                        .insert(*var_index, state_item.clone());
708                }
709            }
710        }
711        result_state
712    }
713
714    pub fn update_callee_report_level(&mut self, unsafe_callee: String, report_level: usize) {
715        self.unsafe_callee_report
716            .entry(unsafe_callee)
717            .and_modify(|e| {
718                if report_level < *e {
719                    *e = report_level;
720                }
721            })
722            .or_insert(report_level);
723    }
724
725    // level: 0 bug_level, 1-3 unsound_level
726    // TODO: add more information about the result
727    pub fn output_results(&self, threshold: usize) {
728        for (unsafe_callee, report_level) in &self.unsafe_callee_report {
729            if *report_level == 0 {
730                rap_warn!("Find one bug in {:?}!", unsafe_callee);
731            } else if *report_level <= threshold {
732                rap_warn!("Find an unsoundness issue in {:?}!", unsafe_callee);
733            }
734        }
735    }
736
737    pub fn insert_path_abstate(
738        &mut self,
739        path_index: usize,
740        place: usize,
741        abitem: AbstractStateItem<'tcx>,
742    ) {
743        self.abstract_states
744            .entry(path_index)
745            .or_insert_with(|| PathInfo {
746                state_map: HashMap::new(),
747            })
748            .state_map
749            .insert(place, abitem);
750    }
751
752    pub fn set_constraint(&mut self, constraint: &Vec<(Place<'tcx>, Place<'tcx>, BinOp)>) {
753        for (p1, p2, op) in constraint {
754            let p1_num = self.handle_proj(false, p1.clone());
755            let p2_num = self.handle_proj(false, p2.clone());
756            self.chains.insert_patial_op(p1_num, p2_num, op);
757        }
758    }
759
760    pub fn get_layout_by_place_usize(&self, place: usize) -> PlaceTy<'tcx> {
761        if let Some(ty) = self.chains.get_obj_ty_through_chain(place) {
762            return self.visit_ty_and_get_layout(ty);
763        } else {
764            return PlaceTy::Unknown;
765        }
766    }
767
768    pub fn visit_ty_and_get_layout(&self, ty: Ty<'tcx>) -> PlaceTy<'tcx> {
769        match ty.kind() {
770            TyKind::RawPtr(ty, _)
771            | TyKind::Ref(_, ty, _)
772            | TyKind::Slice(ty)
773            | TyKind::Array(ty, _) => self.visit_ty_and_get_layout(*ty),
774            TyKind::Param(param_ty) => {
775                let generic_name = param_ty.name.to_string();
776                let mut layout_set: HashSet<(usize, usize)> = HashSet::new();
777                let ty_set = self.generic_map.get(&generic_name.clone());
778                if ty_set.is_none() {
779                    if self.visit_time == 0 {
780                        rap_warn!(
781                            "Can not get generic type set: {:?}, def_id:{:?}",
782                            generic_name,
783                            self.def_id
784                        );
785                    }
786                    return PlaceTy::GenericTy(generic_name, HashSet::new(), layout_set);
787                }
788                for ty in ty_set.unwrap().clone() {
789                    if let PlaceTy::Ty(align, size) = self.visit_ty_and_get_layout(ty) {
790                        layout_set.insert((align, size));
791                    }
792                }
793                return PlaceTy::GenericTy(generic_name, ty_set.unwrap().clone(), layout_set);
794            }
795            TyKind::Adt(def, _list) => {
796                if def.is_enum() {
797                    return PlaceTy::Unknown;
798                } else {
799                    PlaceTy::Unknown
800                }
801            }
802            TyKind::Closure(_, _) => PlaceTy::Unknown,
803            TyKind::Alias(_, ty) => {
804                // rap_warn!("self ty {:?}",ty.self_ty());
805                return self.visit_ty_and_get_layout(ty.self_ty());
806            }
807            _ => {
808                let param_env = self.tcx.param_env(self.def_id);
809                let ty_env = ty::TypingEnv::post_analysis(self.tcx, self.def_id);
810                let input = PseudoCanonicalInput {
811                    typing_env: ty_env,
812                    value: ty,
813                };
814                if let Ok(layout) = self.tcx.layout_of(input) {
815                    // let layout = self.tcx.layout_of(param_env.and(ty)).unwrap();
816                    let align = layout.align.abi.bytes_usize();
817                    let size = layout.size.bytes() as usize;
818                    return PlaceTy::Ty(align, size);
819                } else {
820                    rap_warn!("Find type {:?} that can't get layout!", ty);
821                    PlaceTy::Unknown
822                }
823            }
824        }
825    }
826
827    pub fn get_abstate_by_place_in_path(
828        &self,
829        place: usize,
830        path_index: usize,
831    ) -> AbstractStateItem<'tcx> {
832        if let Some(abstate) = self.abstract_states.get(&path_index) {
833            if let Some(abs) = abstate.state_map.get(&place).cloned() {
834                return abs;
835            }
836        }
837        AbstractStateItem::new_default()
838    }
839
840    pub fn handle_cast(
841        &mut self,
842        rpjc_local: usize,
843        lpjc_local: usize,
844        ty: &Ty<'tcx>,
845        path_index: usize,
846        cast_kind: &CastKind,
847    ) {
848        let mut src_ty = self.get_layout_by_place_usize(rpjc_local);
849        match cast_kind {
850            CastKind::PtrToPtr | CastKind::PointerCoercion(_, _) => {
851                let r_abitem = self.get_abstate_by_place_in_path(rpjc_local, path_index);
852                for state in &r_abitem.state {
853                    if let StateType::AlignState(r_align_state) = state.clone() {
854                        match r_align_state {
855                            AlignState::Cast(from, _to) => {
856                                src_ty = from.clone();
857                            }
858                            _ => {}
859                        }
860                    }
861                }
862
863                let dst_ty = self.visit_ty_and_get_layout(*ty);
864                let align_state =
865                    StateType::AlignState(AlignState::Cast(src_ty.clone(), dst_ty.clone()));
866                let abitem = AbstractStateItem::new(
867                    (Value::None, Value::None),
868                    VType::Pointer(dst_ty),
869                    HashSet::from([align_state]),
870                );
871                self.insert_path_abstate(path_index, lpjc_local, abitem);
872            }
873            _ => {}
874        }
875    }
876
877    pub fn handle_binary_op(
878        &mut self,
879        first_op: &Operand,
880        bin_op: &BinOp,
881        second_op: &Operand,
882        path_index: usize,
883    ) {
884        match bin_op {
885            BinOp::Offset => {
886                let _first_place = get_arg_place(first_op);
887                let _second_place = get_arg_place(second_op);
888            }
889            _ => {}
890        }
891    }
892
893    pub fn handle_proj(&mut self, is_right: bool, place: Place<'tcx>) -> usize {
894        let mut proj_id = place.local.as_usize();
895        for proj in place.projection {
896            match proj {
897                ProjectionElem::Deref => {
898                    proj_id = self.chains.get_point_to_id(place.local.as_usize());
899                    if proj_id == place.local.as_usize() {
900                        proj_id = self.chains.check_ptr(proj_id);
901                    }
902                }
903                ProjectionElem::Field(field, ty) => {
904                    proj_id = self
905                        .chains
906                        .get_field_node_id(proj_id, field.as_usize(), Some(ty));
907                }
908                _ => {}
909            }
910        }
911        proj_id
912    }
913}