rapx/analysis/
unsafety_isolation.rs

1pub mod draw_dot;
2pub mod generate_dot;
3pub mod hir_visitor;
4pub mod isolation_graph;
5pub mod std_unsafety_isolation;
6
7// use crate::analysis::unsafety_isolation::draw_dot::render_dot_graphs;
8use crate::analysis::unsafety_isolation::generate_dot::UigUnit;
9use crate::analysis::unsafety_isolation::hir_visitor::{ContainsUnsafe, RelatedFnCollector};
10use crate::analysis::unsafety_isolation::isolation_graph::*;
11use crate::analysis::utils::fn_info::*;
12use rustc_hir::def_id::DefId;
13use rustc_middle::{
14    mir::{Operand, TerminatorKind},
15    ty,
16    ty::TyCtxt,
17};
18use std::collections::VecDeque;
19
20#[derive(PartialEq)]
21pub enum UigInstruction {
22    Doc,
23    Upg,
24    Ucons,
25    StdSp,
26}
27
28pub struct UnsafetyIsolationCheck<'tcx> {
29    pub tcx: TyCtxt<'tcx>,
30    pub nodes: Vec<IsolationGraphNode>,
31    pub related_func_def_id: Vec<DefId>,
32    pub uigs: Vec<UigUnit>,
33    pub single: Vec<UigUnit>,
34}
35
36impl<'tcx> UnsafetyIsolationCheck<'tcx> {
37    pub fn new(tcx: TyCtxt<'tcx>) -> Self {
38        Self {
39            tcx,
40            nodes: Vec::new(),
41            related_func_def_id: Vec::new(),
42            uigs: Vec::new(),
43            single: Vec::new(),
44        }
45    }
46
47    pub fn start(&mut self, ins: UigInstruction) {
48        if ins == UigInstruction::StdSp {
49            self.handle_std_unsafe();
50            return;
51        } else if ins == UigInstruction::Upg {
52            return; //TODO:
53        }
54        let related_items = RelatedFnCollector::collect(self.tcx);
55        let mut ufunc = 0;
56        for vec in related_items.values() {
57            for (body_id, span) in vec {
58                let (function_unsafe, _block_unsafe) =
59                    ContainsUnsafe::contains_unsafe(self.tcx, *body_id);
60                let def_id = self.tcx.hir_body_owner_def_id(*body_id).to_def_id();
61                if function_unsafe {
62                    ufunc = ufunc + 1;
63                    if ins == UigInstruction::Doc {
64                        self.check_doc(def_id);
65                    }
66                    if ins == UigInstruction::Ucons {
67                        if get_type(self.tcx, def_id) == 0 {
68                            println!(
69                                "Find unsafe constructor: {:?}, location:{:?}.",
70                                def_id, span
71                            );
72                        }
73                    }
74                }
75            }
76        }
77    }
78
79    pub fn check_doc(&self, def_id: DefId) {
80        if !self.check_if_unsafety_doc_exists(def_id) {
81            let visibility = self.tcx.visibility(def_id);
82            println!(
83                "Lack of unsafety doc: {:?}, visibility:{:?}.",
84                self.tcx.def_span(def_id),
85                visibility
86            );
87        }
88    }
89
90    pub fn filter_and_extend_unsafe(&mut self) {
91        let related_items = RelatedFnCollector::collect(self.tcx);
92        let mut queue = VecDeque::new();
93        let mut visited = std::collections::HashSet::new();
94
95        //'related_items' is used for recording whether this api is in crate or not
96        //then init the queue, including all unsafe func and interior unsafe func
97        for vec in related_items.values() {
98            for (body_id, _) in vec {
99                let (function_unsafe, block_unsafe) =
100                    ContainsUnsafe::contains_unsafe(self.tcx, *body_id);
101                let body_did = self.tcx.hir_body_owner_def_id(*body_id).to_def_id();
102                if function_unsafe || block_unsafe {
103                    let node_type = get_type(self.tcx, body_did);
104                    let name = self.get_name(body_did);
105                    let mut new_node =
106                        IsolationGraphNode::new(body_did, node_type, name, function_unsafe, true);
107                    if node_type == 1 {
108                        new_node.constructors = self.search_constructor(body_did);
109                    }
110                    self.nodes.push(new_node);
111                    self.related_func_def_id.push(body_did);
112                    if visited.insert(body_did) {
113                        queue.push_back(body_did);
114                    }
115                }
116            }
117        }
118
119        // BFS handling the queue
120        while let Some(body_did) = queue.pop_front() {
121            if !self.is_crate_api_node(body_did) {
122                continue;
123            }
124            // get all unsafe callees in current crate api and insert to queue
125            let callees = self.visit_node_callees(body_did);
126            for &callee_id in &callees {
127                if visited.insert(callee_id) {
128                    queue.push_back(callee_id);
129                }
130            }
131        }
132    }
133
134    fn check_if_unsafety_doc_exists(&self, def_id: DefId) -> bool {
135        if def_id.krate == rustc_hir::def_id::LOCAL_CRATE {
136            let attrs = self.tcx.get_attrs_unchecked(def_id);
137            for attr in attrs {
138                if attr.is_doc_comment() {
139                    return true;
140                }
141            }
142        }
143        false
144    }
145
146    pub fn check_if_node_exists(&self, body_did: DefId) -> bool {
147        if let Some(_node) = self.nodes.iter().find(|n| n.node_id == body_did) {
148            return true;
149        }
150        false
151    }
152
153    pub fn get_name(&self, body_did: DefId) -> String {
154        let tcx = self.tcx;
155        let mut name = String::new();
156        if let Some(assoc_item) = tcx.opt_associated_item(body_did) {
157            if let Some(impl_id) = assoc_item.impl_container(tcx) {
158                // get struct name
159                let ty = tcx.type_of(impl_id).skip_binder();
160                let type_name = ty.to_string();
161                let type_name = type_name.split('<').next().unwrap_or("").trim();
162                // get method name
163                let method_name = tcx.def_path(body_did).to_string_no_crate_verbose();
164                let method_name = method_name.split("::").last().unwrap_or("");
165                name = format!("{}.{}", type_name, method_name);
166            }
167        } else {
168            let verbose_name = tcx.def_path(body_did).to_string_no_crate_verbose();
169            name = verbose_name.split("::").last().unwrap_or("").to_string();
170        }
171        name
172    }
173
174    pub fn search_constructor(&mut self, def_id: DefId) -> Vec<DefId> {
175        let tcx = self.tcx;
176        let mut constructors = Vec::new();
177        if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
178            if let Some(impl_id) = assoc_item.impl_container(tcx) {
179                // get struct ty
180                let ty = tcx.type_of(impl_id).skip_binder();
181                if let Some(adt_def) = ty.ty_adt_def() {
182                    let adt_def_id = adt_def.did();
183                    let impl_vec = get_impls_for_struct(self.tcx, adt_def_id);
184                    for impl_id in impl_vec {
185                        let associated_items = tcx.associated_items(impl_id);
186                        for item in associated_items.in_definition_order() {
187                            if let ty::AssocKind::Fn {
188                                name: _,
189                                has_self: _,
190                            } = item.kind
191                            {
192                                let item_def_id = item.def_id;
193                                if get_type(self.tcx, item_def_id) == 0 {
194                                    constructors.push(item_def_id);
195                                    self.check_and_insert_node(item_def_id);
196                                    self.set_method_for_constructor(item_def_id, def_id);
197                                }
198                            }
199                        }
200                    }
201                }
202            }
203        }
204        constructors
205    }
206
207    pub fn get_cons_counts(&self, def_id: DefId) -> Vec<DefId> {
208        let tcx = self.tcx;
209        let mut constructors = Vec::new();
210        let mut methods = Vec::new();
211        if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
212            if let Some(impl_id) = assoc_item.impl_container(tcx) {
213                // get struct ty
214                let ty = tcx.type_of(impl_id).skip_binder();
215                if let Some(adt_def) = ty.ty_adt_def() {
216                    let adt_def_id = adt_def.did();
217                    let impl_vec = get_impls_for_struct(self.tcx, adt_def_id);
218                    for impl_id in impl_vec {
219                        let associated_items = tcx.associated_items(impl_id);
220                        for item in associated_items.in_definition_order() {
221                            if let ty::AssocKind::Fn {
222                                name: _,
223                                has_self: _,
224                            } = item.kind
225                            {
226                                let item_def_id = item.def_id;
227                                if get_type(self.tcx, item_def_id) == 0 {
228                                    constructors.push(item_def_id);
229                                } else if get_type(self.tcx, item_def_id) == 1 {
230                                    methods.push(item_def_id);
231                                }
232                            }
233                        }
234                    }
235                }
236                print!("struct:{:?}", ty);
237            }
238        }
239        println!("--------methods:{:?}", methods.len());
240        constructors
241    }
242
243    // visit the func body and record all its unsafe callees and modify visited_tag
244    pub fn visit_node_callees(&mut self, def_id: DefId) -> Vec<DefId> {
245        let mut callees = Vec::new();
246        let tcx = self.tcx;
247        if tcx.is_mir_available(def_id) {
248            let body = tcx.optimized_mir(def_id);
249            for bb in body.basic_blocks.iter() {
250                if let TerminatorKind::Call { func, .. } = &bb.terminator().kind {
251                    if let Operand::Constant(func_constant) = func {
252                        if let ty::FnDef(ref callee_def_id, _) = func_constant.const_.ty().kind() {
253                            if check_safety(self.tcx, *callee_def_id) {
254                                if !callees.contains(callee_def_id) {
255                                    callees.push(*callee_def_id);
256                                    if !self.check_if_node_exists(*callee_def_id) {
257                                        self.check_and_insert_node(*callee_def_id);
258                                        self.set_caller_for_callee(def_id, *callee_def_id);
259                                    }
260                                }
261                            }
262                        }
263                    }
264                }
265            }
266        }
267        if let Some(node) = self.nodes.iter_mut().find(|n| n.node_id == def_id) {
268            node.callees = callees.clone();
269            node.visited_tag = true;
270        }
271        callees
272    }
273
274    pub fn is_crate_api_node(&self, body_did: DefId) -> bool {
275        self.related_func_def_id.contains(&body_did)
276    }
277
278    pub fn check_and_insert_node(&mut self, body_did: DefId) {
279        if self.check_if_node_exists(body_did) {
280            return;
281        }
282        let node_type = get_type(self.tcx, body_did);
283        let name = self.get_name(body_did);
284        let is_crate_api = self.is_crate_api_node(body_did);
285        let node_safety = check_safety(self.tcx, body_did);
286        let mut new_node =
287            IsolationGraphNode::new(body_did, node_type, name, node_safety, is_crate_api);
288        if node_type == 1 {
289            new_node.constructors = self.search_constructor(body_did);
290        }
291        new_node.visited_tag = false;
292        self.nodes.push(new_node);
293    }
294
295    pub fn set_method_for_constructor(&mut self, constructor_did: DefId, method_did: DefId) {
296        if let Some(node) = self
297            .nodes
298            .iter_mut()
299            .find(|node| node.node_id == constructor_did)
300        {
301            if !node.methods.contains(&method_did) {
302                node.methods.push(method_did);
303            }
304        }
305    }
306
307    pub fn set_caller_for_callee(&mut self, caller_did: DefId, callee_did: DefId) {
308        if let Some(node) = self
309            .nodes
310            .iter_mut()
311            .find(|node| node.node_id == callee_did)
312        {
313            if !node.callers.contains(&caller_did) {
314                node.callers.push(caller_did);
315            }
316        }
317    }
318
319    pub fn show_nodes(&self) {
320        for node in &self.nodes {
321            println!(
322                "name:{:?},safety:{:?},calles:{:?}",
323                node.node_name, node.node_unsafety, node.callees
324            );
325        }
326    }
327}