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 }
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 }
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 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 body_visitor.update_fields_states(base_inter_result);
218 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 }
224 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 }
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}