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 }
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 println!("2 {:?}", cons_fields_result.clone());
135 base_inter_result.merge(cons_fields_result);
136 }
137 body_visitor.update_fields_states(base_inter_result);
139 let _mutable_methods = get_all_mutable_methods(self.tcx, def_id);
141 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 }
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}