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 start_analyze_std_func(&mut self) {
94        // let def_id_sets = self.tcx.mir_keys(());
95        let v_fn_def: Vec<_> = rustc_public::find_crates("core")
96            .iter()
97            .flat_map(|krate| krate.fn_defs())
98            .collect();
99        for fn_def in &v_fn_def {
100            let def_id = crate::def_id::to_internal(fn_def, self.tcx);
101            if is_verify_target_func(self.tcx, def_id) {
102                rap_info!(
103                    "Begin verification process for: {:?}",
104                    get_cleaned_def_path_name(self.tcx, def_id)
105                );
106                // TODO: add verify logic
107                // let check_results = self.body_visit_and_check(def_id, &FxHashMap::default());
108                // if !check_results.is_empty() {
109                //     Self::show_check_results(self.tcx, def_id, check_results);
110                // }
111            }
112        }
113    }
114
115    pub fn filter_by_check_level(
116        tcx: TyCtxt<'tcx>,
117        check_level: &CheckLevel,
118        def_id: DefId,
119    ) -> bool {
120        match *check_level {
121            CheckLevel::High => check_visibility(tcx, def_id),
122            _ => true,
123        }
124    }
125
126    pub fn check_soundness(&mut self, def_id: DefId, fn_map: &FxHashMap<DefId, AAResult>) {
127        let check_results = self.body_visit_and_check(def_id, fn_map);
128        let tcx = self.tcx;
129        if !check_results.is_empty() {
130            Self::show_check_results(tcx, def_id, check_results);
131        }
132    }
133
134    pub fn annotate_safety(&self, def_id: DefId) {
135        let annotation_results = self.get_annotation(def_id);
136        if annotation_results.is_empty() {
137            return;
138        }
139        Self::show_annotate_results(self.tcx, def_id, annotation_results);
140    }
141
142    pub fn body_visit_and_check(
143        &mut self,
144        def_id: DefId,
145        fn_map: &FxHashMap<DefId, AAResult>,
146    ) -> Vec<CheckResult> {
147        let mut body_visitor = BodyVisitor::new(self.tcx, def_id, self.global_recorder.clone(), 0);
148        if get_type(self.tcx, def_id) == 1 {
149            let func_cons = get_cons(self.tcx, def_id);
150            let mut base_inter_result = InterResultNode::new_default(get_adt_ty(self.tcx, def_id));
151            for func_con in func_cons {
152                let mut cons_body_visitor =
153                    BodyVisitor::new(self.tcx, func_con.0, self.global_recorder.clone(), 0);
154                let cons_fields_result = cons_body_visitor.path_forward_check(fn_map);
155                // cache and merge fields' states
156                println!("2 {:?}", cons_fields_result.clone());
157                base_inter_result.merge(cons_fields_result);
158            }
159            // update method body's states by constructors' states
160            body_visitor.update_fields_states(base_inter_result);
161            // get mutable methods and TODO: update target method's states
162            let _mutable_methods = get_all_mutable_methods(self.tcx, def_id);
163            // analyze body's states
164            body_visitor.path_forward_check(fn_map);
165        } else {
166            body_visitor.path_forward_check(fn_map);
167        }
168        body_visitor.check_results
169    }
170
171    pub fn body_visit_and_check_uig(&self, def_id: DefId) {
172        let mut uig_checker = UnsafetyIsolationCheck::new(self.tcx);
173        let func_type = get_type(self.tcx, def_id);
174        if func_type == 1 && !self.get_annotation(def_id).is_empty() {
175            let func_cons = uig_checker.search_constructor(def_id);
176            for func_con in func_cons {
177                if check_safety(self.tcx, func_con) {
178                    Self::show_annotate_results(self.tcx, func_con, self.get_annotation(def_id));
179                    // uphold safety to unsafe constructor
180                }
181            }
182        }
183    }
184
185    pub fn get_annotation(&self, def_id: DefId) -> HashSet<String> {
186        let mut results = HashSet::new();
187        if !self.tcx.is_mir_available(def_id) {
188            return results;
189        }
190        let body = self.tcx.optimized_mir(def_id);
191        let basicblocks = &body.basic_blocks;
192        for i in 0..basicblocks.len() {
193            let iter = BasicBlock::from(i);
194            let terminator = basicblocks[iter].terminator.clone().unwrap();
195            if let TerminatorKind::Call {
196                ref func,
197                args: _,
198                destination: _,
199                target: _,
200                unwind: _,
201                call_source: _,
202                fn_span: _,
203            } = terminator.kind
204            {
205                match func {
206                    Operand::Constant(c) => {
207                        if let ty::FnDef(id, ..) = c.ty().kind() {
208                            if !get_sp(self.tcx, *id).is_empty() {
209                                results.extend(get_sp(self.tcx, *id));
210                            } else {
211                                results.extend(self.get_annotation(*id));
212                            }
213                        }
214                    }
215                    _ => {}
216                }
217            }
218        }
219        results
220    }
221
222    pub fn show_check_results(tcx: TyCtxt<'tcx>, def_id: DefId, check_results: Vec<CheckResult>) {
223        rap_info!(
224            "--------In safe function {:?}---------",
225            get_cleaned_def_path_name(tcx, def_id)
226        );
227        for check_result in &check_results {
228            cond_print!(
229                !check_result.failed_contracts.is_empty(),
230                "  Use unsafe api {:?}.",
231                check_result.func_name
232            );
233            for failed_contract in &check_result.failed_contracts {
234                cond_print!(
235                    true,
236                    "      Argument {}'s failed Sps: {:?}",
237                    failed_contract.0,
238                    failed_contract.1
239                );
240            }
241            for passed_contract in &check_result.passed_contracts {
242                cond_print!(
243                    false,
244                    "      Argument {}'s passed Sps: {:?}",
245                    passed_contract.0,
246                    passed_contract.1
247                );
248            }
249        }
250    }
251
252    pub fn show_annotate_results(
253        tcx: TyCtxt<'tcx>,
254        def_id: DefId,
255        annotation_results: HashSet<String>,
256    ) {
257        rap_info!(
258            "--------In unsafe function {:?}---------",
259            get_cleaned_def_path_name(tcx, def_id)
260        );
261        rap_warn!("Lack safety annotations: {:?}.", annotation_results);
262    }
263}