1use std::collections::HashSet;
2
3use super::{
4 contracts::{abstract_state::AlignState, state_lattice::Lattice},
5 matcher::{get_arg_place, UnsafeApi},
6 visitor::{BodyVisitor, CheckResult, PlaceTy},
7};
8use crate::analysis::{
9 senryx::FnMap,
10 utils::fn_info::{display_hashmap, is_strict_ty_convert},
11};
12use crate::{analysis::utils::fn_info::get_cleaned_def_path_name, rap_warn};
13use rustc_hir::def_id::DefId;
14use rustc_middle::mir::Operand;
15use rustc_middle::mir::Place;
16use rustc_span::source_map::Spanned;
17use rustc_span::Span;
18
19impl BodyVisitor<'_> {
20 pub fn handle_std_unsafe_call(
21 &mut self,
22 _dst_place: &Place<'_>,
23 def_id: &DefId,
24 args: &[Spanned<Operand>],
25 _path_index: usize,
26 _fn_map: &FnMap,
27 fn_span: Span,
28 fn_result: UnsafeApi,
29 ) {
30 for (idx, sp_set) in fn_result.sps.iter().enumerate() {
31 if args.is_empty() {
32 break;
33 }
34 let arg_tuple = get_arg_place(&args[idx].node);
35 if arg_tuple.0 {
37 continue;
38 }
39 let arg_place = arg_tuple.1;
40 let _self_func_name = get_cleaned_def_path_name(self.tcx, self.def_id);
41 let func_name = get_cleaned_def_path_name(self.tcx, *def_id);
42 for sp in sp_set {
43 match sp.sp_name.as_str() {
44 "Aligned" => {
45 if !self.check_align(arg_place) {
46 self.insert_failed_check_result(
47 func_name.clone(),
48 fn_span,
49 idx + 1,
50 "Aligned",
51 );
52 } else {
53 self.insert_successful_check_result(
54 func_name.clone(),
55 fn_span,
56 idx + 1,
57 "Aligned",
58 );
59 }
60 }
61 "NonNull" => {
62 if !self.check_non_null(arg_place) {
63 self.insert_failed_check_result(
64 func_name.clone(),
65 fn_span,
66 idx + 1,
67 "NonNull",
68 );
69 } else {
70 self.insert_successful_check_result(
71 func_name.clone(),
72 fn_span,
73 idx + 1,
74 "NonNull",
75 );
76 }
77 }
78 "AllocatorConsistency" => {
79 if !self.check_allocator_consistency(func_name.clone(), arg_place) {
80 self.insert_failed_check_result(
81 func_name.clone(),
82 fn_span,
83 idx + 1,
84 "AllocatorConsistency",
85 );
86 } else {
87 self.insert_successful_check_result(
88 func_name.clone(),
89 fn_span,
90 idx + 1,
91 "AllocatorConsistency",
92 );
93 }
94 }
95 "!ZST" => {
96 if !self.check_non_zst(arg_place) {
97 self.insert_failed_check_result(
98 func_name.clone(),
99 fn_span,
100 idx + 1,
101 "!ZST",
102 );
103 } else {
104 self.insert_successful_check_result(
105 func_name.clone(),
106 fn_span,
107 idx + 1,
108 "!ZST",
109 );
110 }
111 }
112 "Typed" => {
113 if !self.check_typed(arg_place) {
114 self.insert_failed_check_result(
115 func_name.clone(),
116 fn_span,
117 idx + 1,
118 "Typed",
119 );
120 } else {
121 self.insert_successful_check_result(
122 func_name.clone(),
123 fn_span,
124 idx + 1,
125 "Typed",
126 );
127 }
128 }
129 "Allocated" => {
130 if !self.check_allocated(arg_place) {
131 self.insert_failed_check_result(
132 func_name.clone(),
133 fn_span,
134 idx + 1,
135 "Allocated",
136 );
137 } else {
138 self.insert_successful_check_result(
139 func_name.clone(),
140 fn_span,
141 idx + 1,
142 "Allocated",
143 );
144 }
145 }
146 "InBounded" => {
147 if !self.check_inbounded(arg_place) {
148 self.insert_failed_check_result(
149 func_name.clone(),
150 fn_span,
151 idx + 1,
152 "InBounded",
153 );
154 } else {
155 self.insert_successful_check_result(
156 func_name.clone(),
157 fn_span,
158 idx + 1,
159 "InBounded",
160 );
161 }
162 }
163 "ValidString" => {
164 if !self.check_valid_string(arg_place) {
165 self.insert_failed_check_result(
166 func_name.clone(),
167 fn_span,
168 idx + 1,
169 "ValidString",
170 );
171 } else {
172 self.insert_successful_check_result(
173 func_name.clone(),
174 fn_span,
175 idx + 1,
176 "ValidString",
177 );
178 }
179 }
180 "ValidCStr" => {
181 if !self.check_valid_cstr(arg_place) {
182 self.insert_failed_check_result(
183 func_name.clone(),
184 fn_span,
185 idx + 1,
186 "ValidCStr",
187 );
188 } else {
189 self.insert_successful_check_result(
190 func_name.clone(),
191 fn_span,
192 idx + 1,
193 "ValidCStr",
194 );
195 }
196 }
197 "ValidInt" => {
198 if !self.check_valid_num(arg_place) {
199 self.insert_failed_check_result(
200 func_name.clone(),
201 fn_span,
202 idx + 1,
203 "ValidNum",
204 );
205 } else {
206 self.insert_successful_check_result(
207 func_name.clone(),
208 fn_span,
209 idx + 1,
210 "ValidInt",
211 );
212 }
213 }
214 "Init" => {
215 if !self.check_init(arg_place) {
216 self.insert_failed_check_result(
217 func_name.clone(),
218 fn_span,
219 idx + 1,
220 "Init",
221 );
222 } else {
223 self.insert_successful_check_result(
224 func_name.clone(),
225 fn_span,
226 idx + 1,
227 "Init",
228 );
229 }
230 }
231 "ValidPtr" => {
232 if !self.check_valid_ptr(arg_place) {
233 self.insert_failed_check_result(
234 func_name.clone(),
235 fn_span,
236 idx + 1,
237 "ValidPtr",
238 );
239 } else {
240 self.insert_successful_check_result(
241 func_name.clone(),
242 fn_span,
243 idx + 1,
244 "ValidPtr",
245 );
246 }
247 }
248 "Ref2Ptr" => {
249 if !self.check_ref_to_ptr(arg_place) {
250 self.insert_failed_check_result(
251 func_name.clone(),
252 fn_span,
253 idx + 1,
254 "Ref2Ptr",
255 );
256 } else {
257 self.insert_successful_check_result(
258 func_name.clone(),
259 fn_span,
260 idx + 1,
261 "Ref2Ptr",
262 );
263 }
264 }
265 _ => {}
266 }
267 }
268 }
269 }
270
271 pub fn insert_failed_check_result(
272 &mut self,
273 func_name: String,
274 fn_span: Span,
275 idx: usize,
276 sp: &str,
277 ) {
278 if let Some(existing) = self
279 .check_results
280 .iter_mut()
281 .find(|result| result.func_name == func_name && result.func_span == fn_span)
282 {
283 if let Some(passed_set) = existing.passed_contracts.get_mut(&idx) {
284 passed_set.remove(sp);
285 if passed_set.is_empty() {
286 existing.passed_contracts.remove(&idx);
287 }
288 }
289 existing
290 .failed_contracts
291 .entry(idx)
292 .and_modify(|set| {
293 set.insert(sp.to_string());
294 })
295 .or_insert_with(|| {
296 let mut new_set = HashSet::new();
297 new_set.insert(sp.to_string());
298 new_set
299 });
300 } else {
301 let mut new_result = CheckResult::new(&func_name, fn_span);
302 new_result
303 .failed_contracts
304 .insert(idx, HashSet::from([sp.to_string()]));
305 self.check_results.push(new_result);
306 }
307 }
308
309 pub fn insert_successful_check_result(
310 &mut self,
311 func_name: String,
312 fn_span: Span,
313 idx: usize,
314 sp: &str,
315 ) {
316 if let Some(existing) = self
317 .check_results
318 .iter_mut()
319 .find(|result| result.func_name == func_name && result.func_span == fn_span)
320 {
321 if let Some(failed_set) = existing.failed_contracts.get_mut(&idx) {
322 if failed_set.contains(sp) {
323 return;
324 }
325 }
326
327 existing
328 .passed_contracts
329 .entry(idx)
330 .and_modify(|set| {
331 set.insert(sp.to_string());
332 })
333 .or_insert_with(|| HashSet::from([sp.to_string()]));
334 } else {
335 let mut new_result = CheckResult::new(&func_name, fn_span);
336 new_result
337 .passed_contracts
338 .insert(idx, HashSet::from([sp.to_string()]));
339 self.check_results.push(new_result);
340 }
341 }
342
343 pub fn check_align(&self, arg: usize) -> bool {
347 let obj_ty = self.chains.get_obj_ty_through_chain(arg);
348 let var = self.chains.get_var_node(arg);
349 if obj_ty.is_none() || var.is_none() {
350 rap_warn!(
351 "In func {:?}, visitor checker error! Can't get {arg} in chain!",
352 get_cleaned_def_path_name(self.tcx, self.def_id)
353 );
354 display_hashmap(&self.chains.variables, 1);
355 }
356 let ori_ty = self.visit_ty_and_get_layout(obj_ty.unwrap());
357 let cur_ty = self.visit_ty_and_get_layout(var.unwrap().ty.unwrap());
358 let point_to_id = self.chains.get_point_to_id(arg);
359 let var_ty = self.chains.get_var_node(point_to_id);
360 return AlignState::Cast(ori_ty, cur_ty).check() && var_ty.unwrap().states.align;
361 }
362
363 pub fn check_non_zst(&self, arg: usize) -> bool {
364 let obj_ty = self.chains.get_obj_ty_through_chain(arg);
365 if obj_ty.is_none() {
366 rap_warn!(
367 "In func {:?}, visitor checker error! Can't get {arg} in chain!",
368 get_cleaned_def_path_name(self.tcx, self.def_id)
369 );
370 display_hashmap(&self.chains.variables, 1);
371 }
372 let ori_ty = self.visit_ty_and_get_layout(obj_ty.unwrap());
373 match ori_ty {
374 PlaceTy::Ty(_align, size) => size == 0,
375 PlaceTy::GenericTy(_, _, tys) => {
376 if tys.is_empty() {
377 return false;
378 }
379 for (_, size) in tys {
380 if size != 0 {
381 return false;
382 }
383 }
384 true
385 }
386 _ => false,
387 }
388 }
389
390 pub fn check_typed(&self, arg: usize) -> bool {
392 let obj_ty = self.chains.get_obj_ty_through_chain(arg).unwrap();
393 let var = self.chains.get_var_node(arg);
394 let var_ty = var.unwrap().ty.unwrap();
396 if obj_ty != var_ty && is_strict_ty_convert(self.tcx, obj_ty, var_ty) {
397 return false;
398 }
399 self.check_init(arg)
400 }
401
402 pub fn check_non_null(&self, arg: usize) -> bool {
403 let point_to_id = self.chains.get_point_to_id(arg);
404 let var_ty = self.chains.get_var_node(point_to_id);
405 if var_ty.is_none() {
406 rap_warn!(
407 "In func {:?}, visitor checker error! Can't get {arg} in chain!",
408 get_cleaned_def_path_name(self.tcx, self.def_id)
409 );
410 }
411 var_ty.unwrap().states.nonnull
412 }
413
414 pub fn check_init(&self, arg: usize) -> bool {
417 let point_to_id = self.chains.get_point_to_id(arg);
418 let var = self.chains.get_var_node(point_to_id);
419 if var.unwrap().field.is_empty() {
421 let mut init_flag = true;
422 for field in &var.unwrap().field {
423 init_flag &= self.check_init(*field.1);
424 }
425 init_flag
426 } else {
427 var.unwrap().states.init
428 }
429 }
430
431 pub fn check_allocator_consistency(&self, _func_name: String, _arg: usize) -> bool {
432 true
433 }
434
435 pub fn check_allocated(&self, _arg: usize) -> bool {
436 true
437 }
438
439 pub fn check_inbounded(&self, _arg: usize) -> bool {
440 true
441 }
442
443 pub fn check_valid_string(&self, _arg: usize) -> bool {
444 true
445 }
446
447 pub fn check_valid_cstr(&self, _arg: usize) -> bool {
448 true
449 }
450
451 pub fn check_valid_num(&self, _arg: usize) -> bool {
452 true
453 }
454
455 pub fn check_alias(&self, _arg: usize) -> bool {
456 true
457 }
458
459 pub fn check_valid_ptr(&self, arg: usize) -> bool {
461 !self.check_non_zst(arg) || (self.check_non_zst(arg) && self.check_deref(arg))
462 }
463
464 pub fn check_deref(&self, arg: usize) -> bool {
465 self.check_allocated(arg) && self.check_inbounded(arg)
466 }
467
468 pub fn check_ref_to_ptr(&self, arg: usize) -> bool {
469 self.check_deref(arg)
470 && self.check_init(arg)
471 && self.check_align(arg)
472 && self.check_alias(arg)
473 }
474}