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 start_analyze_std_func(&mut self) {
94 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 }
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 println!("2 {:?}", cons_fields_result.clone());
157 base_inter_result.merge(cons_fields_result);
158 }
159 body_visitor.update_fields_states(base_inter_result);
161 let _mutable_methods = get_all_mutable_methods(self.tcx, def_id);
163 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 }
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}