rapx/analysis/upg/
std_upg.rs

1use super::{UPGAnalysis, upg_graph::UPGraph};
2use crate::analysis::{
3    upg::draw_dot::render_dot_graphs,
4    utils::{fn_info::*, show_mir::display_mir},
5};
6use rustc_hir::{Safety, def::DefKind, def_id::DefId};
7use rustc_middle::{
8    mir::Local,
9    ty,
10    ty::{TyCtxt, Visibility},
11};
12use rustc_span::Symbol;
13use std::collections::{HashMap, HashSet};
14
15impl<'tcx> UPGAnalysis<'tcx> {
16    pub fn audit_std_unsafe(&mut self) {
17        let all_std_fn_def = get_all_std_fns_by_rustc_public(self.tcx);
18        // Specific task for vec;
19        let symbol = Symbol::intern("Vec");
20        let vec_def_id = self.tcx.get_diagnostic_item(symbol).unwrap();
21        for &def_id in &all_std_fn_def {
22            let adt_def = get_adt_def_id_by_adt_method(self.tcx, def_id);
23            if adt_def.is_some() && adt_def.unwrap() == vec_def_id {
24                self.insert_upg(def_id);
25            }
26        }
27        self.render_dot();
28    }
29
30    pub fn render_dot(&mut self) {
31        let mut dot_strs = Vec::new();
32        for upg in &self.upgs {
33            let dot_str = UPGraph::generate_dot_from_upg_unit(upg);
34            let caller_name = get_cleaned_def_path_name(self.tcx, upg.caller.def_id);
35            dot_strs.push((caller_name, dot_str));
36        }
37        render_dot_graphs(dot_strs);
38    }
39
40    pub fn get_chains(&mut self) {
41        let v_fn_def = self.tcx.mir_keys(());
42
43        for local_def_id in v_fn_def {
44            let def_id = local_def_id.to_def_id();
45            if !check_visibility(self.tcx, def_id) {
46                continue;
47            }
48            if get_cleaned_def_path_name(self.tcx, def_id) == "std::boxed::Box::<T>::from_raw" {
49                let body = self.tcx.mir_built(local_def_id).steal();
50                display_mir(def_id, &body);
51            }
52            let chains = get_all_std_unsafe_chains(self.tcx, def_id);
53            let valid_chains: Vec<Vec<String>> = chains
54                .into_iter()
55                .filter(|chain| {
56                    if chain.len() > 1 {
57                        return true;
58                    }
59                    if chain.len() == 1 {
60                        if check_safety(self.tcx, def_id) == Safety::Unsafe {
61                            return true;
62                        }
63                    }
64                    false
65                })
66                .collect();
67
68            print_unsafe_chains(&valid_chains);
69        }
70    }
71
72    pub fn get_all_std_unsafe_def_id_by_treat_std_as_local_crate(
73        &mut self,
74        tcx: TyCtxt<'tcx>,
75    ) -> HashSet<DefId> {
76        let mut unsafe_fn = HashSet::default();
77        let mut total_cnt = 0;
78        let mut api_cnt = 0;
79        let mut sp_cnt = 0;
80        let mut sp_count_map: HashMap<String, usize> = HashMap::new();
81        let all_std_fn_def = get_all_std_fns_by_rustc_public(self.tcx);
82
83        for def_id in &all_std_fn_def {
84            if check_safety(tcx, *def_id) == Safety::Unsafe {
85                let sp_set = get_sp(tcx, *def_id);
86                if !sp_set.is_empty() {
87                    unsafe_fn.insert(*def_id);
88                    let mut flag = false;
89                    for sp in &sp_set {
90                        if sp.is_empty()
91                            || sp == "Function_sp"
92                            || sp == "System_sp"
93                            || sp == "ValidSlice"
94                        {
95                            flag = true;
96                        }
97                    }
98                    if !flag {
99                        api_cnt += 1;
100                        sp_cnt += sp_set.len();
101                    }
102                    total_cnt += 1;
103                }
104                for sp in sp_set {
105                    *sp_count_map.entry(sp).or_insert(0) += 1;
106                }
107            }
108            self.insert_upg(*def_id);
109        }
110
111        rap_info!(
112            "fn_def : {}, count : {:?} and {:?}, sp cnt : {}",
113            all_std_fn_def.len(),
114            total_cnt,
115            api_cnt,
116            sp_cnt
117        );
118        unsafe_fn
119    }
120
121    pub fn check_params(&self, def_id: DefId) {
122        let body = self.tcx.optimized_mir(def_id);
123        let locals = body.local_decls.clone();
124        let fn_sig = self.tcx.fn_sig(def_id).skip_binder();
125        let param_len = fn_sig.inputs().skip_binder().len();
126        let return_ty = fn_sig.output().skip_binder();
127        for idx in 1..param_len + 1 {
128            let local_ty = locals[Local::from(idx)].ty;
129            if is_ptr(local_ty) && !return_ty.is_unit() {
130                println!("{:?}", get_cleaned_def_path_name(self.tcx, def_id));
131            }
132        }
133    }
134
135    pub fn analyze_upg(&self) {
136        let mut fn_no_callee = Vec::new();
137        let mut fn_unsafe_caller = Vec::new();
138        let mut fn_safe_caller = Vec::new();
139        let mut method_no_callee = Vec::new();
140        let mut method_unsafe_caller = Vec::new();
141        let mut method_safe_caller = Vec::new();
142        for upg in &self.upgs {
143            if upg.caller.fn_kind == FnKind::Method {
144                // method
145                if upg.callees.is_empty() {
146                    method_no_callee.push(upg.clone());
147                }
148                if upg.caller.fn_safety == Safety::Unsafe {
149                    method_unsafe_caller.push(upg.clone());
150                } else {
151                    method_safe_caller.push(upg.clone());
152                }
153            } else {
154                //function
155                if upg.callees.is_empty() {
156                    fn_no_callee.push(upg.clone());
157                }
158                if upg.caller.fn_safety == Safety::Unsafe {
159                    fn_unsafe_caller.push(upg.clone());
160                } else {
161                    fn_safe_caller.push(upg.clone());
162                }
163            }
164        }
165        rap_info!(
166            "func: {},{},{}, method: {},{},{}",
167            fn_no_callee.len(),
168            fn_unsafe_caller.len(),
169            fn_safe_caller.len(),
170            method_no_callee.len(),
171            method_unsafe_caller.len(),
172            method_safe_caller.len()
173        );
174        rap_info!("units: {}", self.upgs.len());
175    }
176
177    pub fn analyze_struct(&self) {
178        let mut cache = HashSet::default();
179        let mut s = 0;
180        let mut u = 0;
181        let mut e = 0;
182        let mut uc = 0;
183        let mut vi = 0;
184        for upg in &self.upgs {
185            self.get_struct(
186                upg.caller.def_id,
187                &mut cache,
188                &mut s,
189                &mut u,
190                &mut e,
191                &mut uc,
192                &mut vi,
193            );
194        }
195        println!("{},{},{},{}", s, u, e, vi);
196    }
197
198    pub fn get_struct(
199        &self,
200        def_id: DefId,
201        cache: &mut HashSet<DefId>,
202        s: &mut usize,
203        u: &mut usize,
204        e: &mut usize,
205        uc: &mut usize,
206        vi: &mut usize,
207    ) {
208        let tcx = self.tcx;
209        let mut safe_constructors = Vec::new();
210        let mut unsafe_constructors = Vec::new();
211        let mut unsafe_methods = Vec::new();
212        let mut safe_methods = Vec::new();
213        let mut mut_methods = Vec::new();
214        let mut struct_name = "".to_string();
215        let mut ty_flag = 0;
216        let mut vi_flag = false;
217        if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
218            if let Some(impl_id) = assoc_item.impl_container(tcx) {
219                // get struct ty
220                let ty = tcx.type_of(impl_id).skip_binder();
221                if let Some(adt_def) = ty.ty_adt_def() {
222                    if adt_def.is_union() {
223                        ty_flag = 1;
224                    } else if adt_def.is_enum() {
225                        ty_flag = 2;
226                    }
227                    let adt_def_id = adt_def.did();
228                    struct_name = get_cleaned_def_path_name(tcx, adt_def_id);
229                    if !cache.insert(adt_def_id) {
230                        return;
231                    }
232
233                    vi_flag = false;
234                    let impl_vec = get_impls_for_struct(self.tcx, adt_def_id);
235                    for impl_id in impl_vec {
236                        let associated_items = tcx.associated_items(impl_id);
237                        for item in associated_items.in_definition_order() {
238                            if let ty::AssocKind::Fn {
239                                name: _,
240                                has_self: _,
241                            } = item.kind
242                            {
243                                let item_def_id = item.def_id;
244                                if !get_sp(self.tcx, item_def_id).is_empty() {
245                                    vi_flag = true;
246                                }
247                                if get_type(self.tcx, item_def_id) == FnKind::Constructor
248                                    && check_safety(self.tcx, item_def_id) == Safety::Unsafe
249                                // && get_sp(self.tcx, item_def_id).len() > 0
250                                {
251                                    unsafe_constructors.push(item_def_id);
252                                }
253                                if get_type(self.tcx, item_def_id) == FnKind::Constructor
254                                    && check_safety(self.tcx, item_def_id) == Safety::Safe
255                                {
256                                    safe_constructors.push(item_def_id);
257                                }
258                                if get_type(self.tcx, item_def_id) == FnKind::Method
259                                    && check_safety(self.tcx, item_def_id) == Safety::Unsafe
260                                // && get_sp(self.tcx, item_def_id).len() > 0
261                                {
262                                    unsafe_methods.push(item_def_id);
263                                }
264                                if get_type(self.tcx, item_def_id) == FnKind::Method
265                                    && check_safety(self.tcx, item_def_id) == Safety::Safe
266                                {
267                                    if !get_unsafe_callees(tcx, item_def_id).is_empty() {
268                                        safe_methods.push(item_def_id);
269                                    }
270                                }
271                                if get_type(self.tcx, item_def_id) == FnKind::Method
272                                    && has_mut_self_param(self.tcx, item_def_id)
273                                {
274                                    mut_methods.push(item_def_id);
275                                }
276                            }
277                        }
278                    }
279                }
280            }
281        }
282        if struct_name == *""
283            || (unsafe_constructors.is_empty()
284                && unsafe_methods.is_empty()
285                && safe_methods.is_empty())
286        {
287            return;
288        }
289        if vi_flag {
290            *vi += 1;
291        }
292        if !unsafe_constructors.is_empty() {
293            *uc += 1;
294        }
295        if ty_flag == 0 {
296            *s += 1;
297        }
298        if ty_flag == 1 {
299            *u += 1;
300        }
301        if ty_flag == 2 {
302            *e += 1;
303        }
304
305        println!("Safe Cons: {}", safe_constructors.len());
306        for safe_cons in safe_constructors {
307            println!(" {:?}", get_cleaned_def_path_name(tcx, safe_cons));
308        }
309        println!("Unsafe Cons: {}", unsafe_constructors.len());
310        for unsafe_cons in unsafe_constructors {
311            println!(" {:?}", get_cleaned_def_path_name(tcx, unsafe_cons));
312        }
313        println!("Unsafe Methods: {}", unsafe_methods.len());
314        for method in unsafe_methods {
315            println!(" {:?}", get_cleaned_def_path_name(tcx, method));
316        }
317        println!("Safe Methods with unsafe callee: {}", safe_methods.len());
318        for method in safe_methods {
319            println!(" {:?}", get_cleaned_def_path_name(tcx, method));
320        }
321        println!("Mut self Methods: {}", mut_methods.len());
322        for method in mut_methods {
323            println!(" {:?}", get_cleaned_def_path_name(tcx, method));
324        }
325    }
326
327    pub fn get_units_data(&mut self, tcx: TyCtxt<'tcx>) {
328        // [uf/um, sf-uf, sf-um, uf-uf, uf-um, um(sf)-uf, um(uf)-uf, um(sf)-um, um(uf)-um, sm(sf)-uf, sm(uf)-uf, sm(sf)-um, sm(uf)-um]
329        let mut basic_units_data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
330        let def_id_sets = tcx.mir_keys(());
331        for local_def_id in def_id_sets {
332            let def_id = local_def_id.to_def_id();
333            if tcx.def_kind(def_id) == DefKind::Fn || tcx.def_kind(def_id) == DefKind::AssocFn {
334                self.insert_upg(def_id);
335            }
336        }
337        for upg in &self.upgs {
338            upg.count_basic_units(&mut basic_units_data);
339        }
340    }
341
342    pub fn process_def_id(
343        &mut self,
344        def_id: DefId,
345        visited: &mut HashSet<DefId>,
346        unsafe_fn: &mut HashSet<DefId>,
347    ) {
348        if !visited.insert(def_id) {
349            return;
350        }
351        match self.tcx.def_kind(def_id) {
352            DefKind::Fn | DefKind::AssocFn => {
353                if check_safety(self.tcx, def_id) == Safety::Unsafe
354                    && self.tcx.visibility(def_id) == Visibility::Public
355                {
356                    unsafe_fn.insert(def_id);
357                    self.insert_upg(def_id);
358                }
359            }
360            DefKind::Mod => {
361                for child in self.tcx.module_children(def_id) {
362                    if let Some(child_def_id) = child.res.opt_def_id() {
363                        self.process_def_id(child_def_id, visited, unsafe_fn);
364                    }
365                }
366            }
367            DefKind::Impl { of_trait: _ } => {
368                for item in self.tcx.associated_item_def_ids(def_id) {
369                    self.process_def_id(*item, visited, unsafe_fn);
370                }
371            }
372            DefKind::Struct => {
373                let impls = self.tcx.inherent_impls(def_id);
374                for impl_def_id in impls {
375                    self.process_def_id(*impl_def_id, visited, unsafe_fn);
376                }
377            }
378            DefKind::Ctor(_of, _kind) => {
379                if self.tcx.is_mir_available(def_id) {
380                    let _mir = self.tcx.optimized_mir(def_id);
381                }
382            }
383            _ => {
384                // println!("{:?}",tcx.def_kind(def_id));
385            }
386        }
387    }
388}