rapx/analysis/senryx/
visitor_check.rs

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 this arg is a constant
36            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    // ----------------------Sp checking functions--------------------------
344
345    // TODO: Currently can not support unaligned offset checking
346    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    // checking the value ptr points to is valid for its type
391    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        // display_hashmap(&self.chains.variables, 1);
395        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    // check each field's init state in the tree.
415    // check arg itself when it doesn't have fields.
416    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        // display_hashmap(&self.chains.variables, 1);
420        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    // Compound SPs
460    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}