rapx/analysis/senryx/
mod.rs

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