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 }
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 println!("2 {:?}", cons_fields_result.clone());
126 base_inter_result.merge(cons_fields_result);
127 }
128 body_visitor.update_fields_states(base_inter_result);
130 let _mutable_methods = get_all_mutable_methods(self.tcx, def_id);
132 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 }
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}