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;
8pub mod symbolic_analysis;
9#[allow(unused)]
10pub mod visitor;
11#[allow(unused)]
12pub mod visitor_check;
13use dominated_graph::InterResultNode;
14use inter_record::InterAnalysisRecord;
15use rustc_data_structures::fx::FxHashMap;
16use rustc_hir::def_id::DefId;
17use rustc_middle::{
18    mir::{BasicBlock, Operand, TerminatorKind},
19    ty::{self, TyCtxt},
20};
21use std::collections::{HashMap, HashSet};
22use visitor::{BodyVisitor, CheckResult};
23
24use crate::{
25    analysis::{
26        Analysis,
27        core::alias_analysis::{AAResult, AliasAnalysis, default::AliasAnalyzer},
28        unsafety_isolation::{
29            UnsafetyIsolationCheck,
30            hir_visitor::{ContainsUnsafe, RelatedFnCollector},
31        },
32        utils::fn_info::*,
33    },
34    rap_info, rap_warn,
35};
36
37macro_rules! cond_print {
38    ($cond:expr, $($t:tt)*) => {if $cond {rap_warn!($($t)*)} else {rap_info!($($t)*)}};
39}
40
41pub enum CheckLevel {
42    High,
43    Medium,
44    Low,
45}
46
47pub struct SenryxCheck<'tcx> {
48    pub tcx: TyCtxt<'tcx>,
49    pub threshhold: usize,
50    pub global_recorder: HashMap<DefId, InterAnalysisRecord<'tcx>>,
51}
52
53impl<'tcx> SenryxCheck<'tcx> {
54    pub fn new(tcx: TyCtxt<'tcx>, threshhold: usize) -> Self {
55        Self {
56            tcx,
57            threshhold,
58            global_recorder: HashMap::new(),
59        }
60    }
61
62    pub fn start(&mut self, check_level: CheckLevel, is_verify: bool) {
63        let tcx = self.tcx;
64        let mut analyzer = AliasAnalyzer::new(self.tcx);
65        analyzer.run();
66        let fn_map = &analyzer.get_all_fn_alias();
67        let related_items = RelatedFnCollector::collect(tcx);
68        for vec in related_items.clone().values() {
69            for (body_id, _span) in vec {
70                let (function_unsafe, block_unsafe) =
71                    ContainsUnsafe::contains_unsafe(tcx, *body_id);
72                let def_id = tcx.hir_body_owner_def_id(*body_id).to_def_id();
73                let std_unsafe_callee = get_all_std_unsafe_callees(self.tcx, def_id);
74                if !Self::filter_by_check_level(tcx, &check_level, def_id) {
75                    continue;
76                }
77                if block_unsafe && is_verify && !std_unsafe_callee.is_empty() {
78                    self.check_soundness(def_id, fn_map);
79                }
80                if function_unsafe && !is_verify && !std_unsafe_callee.is_empty() {
81                    // self.annotate_safety(def_id);
82                    // let mutable_methods = get_all_mutable_methods(self.tcx, def_id);
83                    // println!("mutable_methods: {:?}", mutable_methods);
84                }
85            }
86        }
87    }
88
89    pub fn start_analyze_std_func(&mut self) {
90        let v_fn_def: Vec<_> = rustc_public::find_crates("alloc")
91            .iter()
92            .flat_map(|krate| krate.fn_defs())
93            .collect();
94        for fn_def in &v_fn_def {
95            let def_id = crate::def_id::to_internal(fn_def, self.tcx);
96            if is_verify_target_func(self.tcx, def_id) {
97                rap_info!(
98                    "Begin verification process for: {:?}",
99                    get_cleaned_def_path_name(self.tcx, def_id)
100                );
101                let check_results = self.body_visit_and_check(def_id, &FxHashMap::default());
102                if !check_results.is_empty() {
103                    Self::show_check_results(self.tcx, def_id, check_results);
104                }
105            }
106        }
107    }
108
109    pub fn start_analyze_std_func_chains(&mut self) {
110        let all_std_fn_def = get_all_std_fns_by_rustc_public(self.tcx);
111        let mut last_nodes = HashSet::new();
112        for &def_id in &all_std_fn_def {
113            if !check_visibility(self.tcx, def_id) {
114                continue;
115            }
116            let chains = get_all_std_unsafe_chains(self.tcx, def_id);
117            let valid_chains: Vec<Vec<String>> = chains
118                .into_iter()
119                .filter(|chain| {
120                    if chain.len() > 1 {
121                        return true;
122                    }
123                    if chain.len() == 1 {
124                        let is_unsafe = check_safety(self.tcx, def_id);
125                        return is_unsafe;
126                    }
127                    false
128                })
129                .collect();
130
131            let mut last = true;
132            for chain in &valid_chains {
133                if let Some(last_node) = chain.last() {
134                    if !last_node.contains("intrinsic") && !last_node.contains("aarch64") {
135                        last_nodes.insert(last_node.clone());
136                        last = false;
137                    }
138                }
139            }
140            if last {
141                continue;
142            }
143            // print_unsafe_chains(&valid_chains);
144        }
145        Self::print_last_nodes(&last_nodes);
146    }
147
148    pub fn print_last_nodes(last_nodes: &HashSet<String>) {
149        if last_nodes.is_empty() {
150            println!("No unsafe call chain last nodes found.");
151            return;
152        }
153
154        println!(
155            "Found {} unique unsafe call chain last nodes:",
156            last_nodes.len()
157        );
158        for (i, node) in last_nodes.iter().enumerate() {
159            println!("{}. {}", i + 1, node);
160        }
161    }
162
163    pub fn filter_by_check_level(
164        tcx: TyCtxt<'tcx>,
165        check_level: &CheckLevel,
166        def_id: DefId,
167    ) -> bool {
168        match *check_level {
169            CheckLevel::High => check_visibility(tcx, def_id),
170            _ => true,
171        }
172    }
173
174    pub fn check_soundness(&mut self, def_id: DefId, fn_map: &FxHashMap<DefId, AAResult>) {
175        let check_results = self.body_visit_and_check(def_id, fn_map);
176        let tcx = self.tcx;
177        if !check_results.is_empty() {
178            Self::show_check_results(tcx, def_id, check_results);
179        }
180    }
181
182    pub fn annotate_safety(&self, def_id: DefId) {
183        let annotation_results = self.get_annotation(def_id);
184        if annotation_results.is_empty() {
185            return;
186        }
187        Self::show_annotate_results(self.tcx, def_id, annotation_results);
188    }
189
190    pub fn body_visit_and_check(
191        &mut self,
192        def_id: DefId,
193        fn_map: &FxHashMap<DefId, AAResult>,
194    ) -> Vec<CheckResult> {
195        let mut body_visitor = BodyVisitor::new(self.tcx, def_id, self.global_recorder.clone(), 0);
196        let target_name = get_cleaned_def_path_name(self.tcx, def_id);
197        if !target_name.contains("into_raw_parts_with_alloc") {
198            return body_visitor.check_results;
199        }
200        rap_info!("Begin verification process for: {:?}", target_name);
201        if get_type(self.tcx, def_id) == 1 {
202            let func_cons = get_cons(self.tcx, def_id);
203            let mut base_inter_result = InterResultNode::new_default(get_adt_ty(self.tcx, def_id));
204            for func_con in func_cons {
205                let mut cons_body_visitor =
206                    BodyVisitor::new(self.tcx, func_con.0, self.global_recorder.clone(), 0);
207                let cons_fields_result = cons_body_visitor.path_forward_check(fn_map);
208                // cache and merge fields' states
209                let cons_name = get_cleaned_def_path_name(self.tcx, func_con.0);
210                println!(
211                    "cons {cons_name} state results {:?}",
212                    cons_fields_result.clone()
213                );
214                base_inter_result.merge(cons_fields_result);
215            }
216            // update method body's states by constructors' states
217            body_visitor.update_fields_states(base_inter_result);
218            // get mutable methods and TODO: update target method's states
219            let mutable_methods = get_all_mutable_methods(self.tcx, def_id);
220            for mm in mutable_methods {
221                println!("mut method {:?}", get_cleaned_def_path_name(self.tcx, mm.0));
222                // has_tainted_fields(self.tcx, mm.0, 1);
223            }
224            // analyze body's states
225            body_visitor.path_forward_check(fn_map);
226        } else {
227            body_visitor.path_forward_check(fn_map);
228        }
229        body_visitor.check_results
230    }
231
232    pub fn body_visit_and_check_uig(&self, def_id: DefId) {
233        let mut uig_checker = UnsafetyIsolationCheck::new(self.tcx);
234        let func_type = get_type(self.tcx, def_id);
235        if func_type == 1 && !self.get_annotation(def_id).is_empty() {
236            let func_cons = uig_checker.search_constructor(def_id);
237            for func_con in func_cons {
238                if check_safety(self.tcx, func_con) {
239                    Self::show_annotate_results(self.tcx, func_con, self.get_annotation(def_id));
240                    // uphold safety to unsafe constructor
241                }
242            }
243        }
244    }
245
246    pub fn get_annotation(&self, def_id: DefId) -> HashSet<String> {
247        let mut results = HashSet::new();
248        if !self.tcx.is_mir_available(def_id) {
249            return results;
250        }
251        let body = self.tcx.optimized_mir(def_id);
252        let basicblocks = &body.basic_blocks;
253        for i in 0..basicblocks.len() {
254            let iter = BasicBlock::from(i);
255            let terminator = basicblocks[iter].terminator.clone().unwrap();
256            if let TerminatorKind::Call {
257                ref func,
258                args: _,
259                destination: _,
260                target: _,
261                unwind: _,
262                call_source: _,
263                fn_span: _,
264            } = terminator.kind
265            {
266                match func {
267                    Operand::Constant(c) => {
268                        if let ty::FnDef(id, ..) = c.ty().kind() {
269                            if !get_sp(self.tcx, *id).is_empty() {
270                                results.extend(get_sp(self.tcx, *id));
271                            } else {
272                                results.extend(self.get_annotation(*id));
273                            }
274                        }
275                    }
276                    _ => {}
277                }
278            }
279        }
280        results
281    }
282
283    pub fn show_check_results(tcx: TyCtxt<'tcx>, def_id: DefId, check_results: Vec<CheckResult>) {
284        rap_info!(
285            "--------In safe function {:?}---------",
286            get_cleaned_def_path_name(tcx, def_id)
287        );
288        for check_result in &check_results {
289            cond_print!(
290                !check_result.failed_contracts.is_empty(),
291                "  Use unsafe api {:?}.",
292                check_result.func_name
293            );
294            for failed_contract in &check_result.failed_contracts {
295                cond_print!(
296                    true,
297                    "      Argument {}'s failed Sps: {:?}",
298                    failed_contract.0,
299                    failed_contract.1
300                );
301            }
302            for passed_contract in &check_result.passed_contracts {
303                cond_print!(
304                    false,
305                    "      Argument {}'s passed Sps: {:?}",
306                    passed_contract.0,
307                    passed_contract.1
308                );
309            }
310        }
311    }
312
313    pub fn show_annotate_results(
314        tcx: TyCtxt<'tcx>,
315        def_id: DefId,
316        annotation_results: HashSet<String>,
317    ) {
318        rap_info!(
319            "--------In unsafe function {:?}---------",
320            get_cleaned_def_path_name(tcx, def_id)
321        );
322        rap_warn!("Lack safety annotations: {:?}.", annotation_results);
323    }
324}