rapx/analysis/senryx/
visitor.rs

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