rapx/analysis/
unsafety_isolation.rs1pub mod draw_dot;
2pub mod generate_dot;
3pub mod hir_visitor;
4pub mod isolation_graph;
5pub mod std_unsafety_isolation;
6
7use 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; }
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 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 while let Some(body_did) = queue.pop_front() {
121 if !self.is_crate_api_node(body_did) {
122 continue;
123 }
124 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 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 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 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 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 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}