rapx/analysis/
senryx.rs

1pub mod contracts;
2#[allow(unused)]
3pub mod dominated_chain;
4pub mod generic_check;
5pub mod inter_record;
6pub mod matcher;
7#[allow(unused)]
8pub mod visitor;
9pub mod visitor_check;
10use crate::analysis::utils::fn_info::*;
11use crate::{
12    analysis::unsafety_isolation::{
13        hir_visitor::{ContainsUnsafe, RelatedFnCollector},
14        UnsafetyIsolationCheck,
15    },
16    rap_info, rap_warn,
17};
18use dominated_chain::InterResultNode;
19use inter_record::InterAnalysisRecord;
20use rustc_hir::def_id::DefId;
21use rustc_middle::mir::{BasicBlock, Operand, TerminatorKind};
22use rustc_middle::ty;
23use rustc_middle::ty::TyCtxt;
24use std::collections::{HashMap, HashSet};
25use visitor::{BodyVisitor, CheckResult};
26
27use super::core::alias::mop::{FnMap, MopAlias};
28use super::core::alias::AliasAnalysis;
29use super::Analysis;
30
31macro_rules! cond_print {
32    ($cond:expr, $($t:tt)*) => {if $cond {rap_warn!($($t)*)} else {rap_info!($($t)*)}};
33}
34
35pub enum CheckLevel {
36    High,
37    Medium,
38    Low,
39}
40
41pub struct SenryxCheck<'tcx> {
42    pub tcx: TyCtxt<'tcx>,
43    pub threshhold: usize,
44    pub global_recorder: HashMap<DefId, InterAnalysisRecord<'tcx>>,
45}
46
47impl<'tcx> SenryxCheck<'tcx> {
48    pub fn new(tcx: TyCtxt<'tcx>, threshhold: usize) -> Self {
49        Self {
50            tcx,
51            threshhold,
52            global_recorder: HashMap::new(),
53        }
54    }
55
56    pub fn start(&mut self, check_level: CheckLevel, is_verify: bool) {
57        let tcx = self.tcx;
58        let mut mop = MopAlias::new(self.tcx);
59        mop.run();
60        let fn_map = &mop.get_all_fn_alias();
61        let related_items = RelatedFnCollector::collect(tcx);
62        for vec in related_items.clone().values() {
63            for (body_id, _span) in vec {
64                let (function_unsafe, block_unsafe) =
65                    ContainsUnsafe::contains_unsafe(tcx, *body_id);
66                let def_id = tcx.hir_body_owner_def_id(*body_id).to_def_id();
67                if !Self::filter_by_check_level(tcx, &check_level, def_id) {
68                    continue;
69                }
70                if block_unsafe
71                    && is_verify
72                    && !get_all_std_unsafe_callees(self.tcx, def_id).is_empty()
73                {
74                    self.check_soundness(def_id, fn_map);
75                }
76                if function_unsafe
77                    && !is_verify
78                    && !get_all_std_unsafe_callees(self.tcx, def_id).is_empty()
79                {
80                    self.annotate_safety(def_id);
81                    // let mutable_methods = get_all_mutable_methods(self.tcx, def_id);
82                    // println!("mutable_methods: {:?}", mutable_methods);
83                }
84            }
85        }
86    }
87
88    pub fn filter_by_check_level(
89        tcx: TyCtxt<'tcx>,
90        check_level: &CheckLevel,
91        def_id: DefId,
92    ) -> bool {
93        match *check_level {
94            CheckLevel::High => check_visibility(tcx, def_id),
95            _ => true,
96        }
97    }
98
99    pub fn check_soundness(&mut self, def_id: DefId, fn_map: &FnMap) {
100        let check_results = self.body_visit_and_check(def_id, fn_map);
101        let tcx = self.tcx;
102        if !check_results.is_empty() {
103            Self::show_check_results(tcx, def_id, check_results);
104        }
105    }
106
107    pub fn annotate_safety(&self, def_id: DefId) {
108        let annotation_results = self.get_annotation(def_id);
109        if annotation_results.is_empty() {
110            return;
111        }
112        Self::show_annotate_results(self.tcx, def_id, annotation_results);
113    }
114
115    pub fn body_visit_and_check(&mut self, def_id: DefId, fn_map: &FnMap) -> Vec<CheckResult> {
116        let mut body_visitor = BodyVisitor::new(self.tcx, def_id, self.global_recorder.clone(), 0);
117        if get_type(self.tcx, def_id) == 1 {
118            let func_cons = get_cons(self.tcx, def_id);
119            let mut base_inter_result = InterResultNode::new_default(get_adt_ty(self.tcx, def_id));
120            for func_con in func_cons {
121                let mut cons_body_visitor =
122                    BodyVisitor::new(self.tcx, func_con.0, self.global_recorder.clone(), 0);
123                let cons_fields_result = cons_body_visitor.path_forward_check(fn_map);
124                // cache and merge fields' states
125                println!("2 {:?}", cons_fields_result.clone());
126                base_inter_result.merge(cons_fields_result);
127            }
128            // update method body's states by constructors' states
129            body_visitor.update_fields_states(base_inter_result);
130            // get mutable methods and TODO: update target method's states
131            let _mutable_methods = get_all_mutable_methods(self.tcx, def_id);
132            // analyze body's states
133            body_visitor.path_forward_check(fn_map);
134        } else {
135            body_visitor.path_forward_check(fn_map);
136        }
137        body_visitor.check_results
138    }
139
140    pub fn body_visit_and_check_uig(&self, def_id: DefId) {
141        let mut uig_checker = UnsafetyIsolationCheck::new(self.tcx);
142        let func_type = get_type(self.tcx, def_id);
143        if func_type == 1 && !self.get_annotation(def_id).is_empty() {
144            let func_cons = uig_checker.search_constructor(def_id);
145            for func_con in func_cons {
146                if check_safety(self.tcx, func_con) {
147                    Self::show_annotate_results(self.tcx, func_con, self.get_annotation(def_id));
148                    // uphold safety to unsafe constructor
149                }
150            }
151        }
152    }
153
154    pub fn get_annotation(&self, def_id: DefId) -> HashSet<String> {
155        let mut results = HashSet::new();
156        if !self.tcx.is_mir_available(def_id) {
157            return results;
158        }
159        let body = self.tcx.optimized_mir(def_id);
160        let basicblocks = &body.basic_blocks;
161        for i in 0..basicblocks.len() {
162            let iter = BasicBlock::from(i);
163            let terminator = basicblocks[iter].terminator.clone().unwrap();
164            if let TerminatorKind::Call {
165                ref func,
166                args: _,
167                destination: _,
168                target: _,
169                unwind: _,
170                call_source: _,
171                fn_span: _,
172            } = terminator.kind
173            {
174                match func {
175                    Operand::Constant(c) => {
176                        if let ty::FnDef(id, ..) = c.ty().kind() {
177                            if get_sp(self.tcx, *id).is_empty() {
178                                results.extend(get_sp(self.tcx, *id));
179                            } else {
180                                results.extend(self.get_annotation(*id));
181                            }
182                        }
183                    }
184                    _ => {}
185                }
186            }
187        }
188        results
189    }
190
191    pub fn show_check_results(tcx: TyCtxt<'tcx>, def_id: DefId, check_results: Vec<CheckResult>) {
192        rap_info!(
193            "--------In safe function {:?}---------",
194            get_cleaned_def_path_name(tcx, def_id)
195        );
196        for check_result in &check_results {
197            cond_print!(
198                !check_result.failed_contracts.is_empty(),
199                "  Use unsafe api {:?}.",
200                check_result.func_name
201            );
202            for failed_contract in &check_result.failed_contracts {
203                cond_print!(
204                    !check_result.failed_contracts.is_empty(),
205                    "      Argument {}'s failed Sps: {:?}",
206                    failed_contract.0,
207                    failed_contract.1
208                );
209            }
210        }
211    }
212
213    pub fn show_annotate_results(
214        tcx: TyCtxt<'tcx>,
215        def_id: DefId,
216        annotation_results: HashSet<String>,
217    ) {
218        rap_info!(
219            "--------In unsafe function {:?}---------",
220            get_cleaned_def_path_name(tcx, def_id)
221        );
222        rap_warn!("Lack safety annotations: {:?}.", annotation_results);
223    }
224}