1use super::{bug_records::*, checks, corner_case::*, drop::*, graph::*};
2use super::observer::SafeDropObserver;
3use crate::{
4 analysis::alias::default::MopFnAliasMap,
5 analysis::path::{PathNode, PathTree},
6 def_id::is_drop_fn,
7 utils::source::{get_filename, get_name},
8};
9use rustc_middle::{
10 mir::{
11 Operand::{self},
12 Place, TerminatorKind,
13 },
14 ty::{self},
15};
16use rustc_span::{Span, Symbol};
17
18pub const VISIT_LIMIT: usize = 1000;
19
20impl<'tcx> SafeDropGraph<'tcx> {
21 fn dfs_safedrop(
22 &mut self,
23 node: &PathNode,
24 path: &mut Vec<usize>,
25 fn_map: &MopFnAliasMap,
26 ) -> Result<(), ()> {
27 path.push(node.block);
28 {
29 let mut obs = SafeDropObserver {
30 drop_record: &mut self.drop_record,
31 bug_records: &mut self.bug_records,
32 current_bb: node.block,
33 };
34 self.alias_graph.alias_bb(node.block, &mut obs);
35 self.alias_graph.alias_bbcall(node.block, fn_map, &mut obs);
36 }
37 self.drop_check(node.block);
38
39 let saved_values = self.alias_graph.values.clone();
40 let saved_pts_graph = self.alias_graph.pts_graph.clone();
41 let saved_drop_record = self.drop_record.clone();
42
43 if node.is_path_end {
44 self.alias_graph.increment_visit_times();
45 if self.alias_graph.visit_times() > VISIT_LIMIT {
46 path.pop();
47 return Err(());
48 }
49 if should_check(self.alias_graph.def_id()) {
50 if let Some(&last) = path.last() {
51 let cfg_block = self.alias_graph.cfg_block(last).clone();
52 self.dp_check(cfg_block.is_cleanup);
53 }
54 }
55 }
56
57 for child in &node.children {
58 self.alias_graph.values = saved_values.clone();
59 self.alias_graph.pts_graph = saved_pts_graph.clone();
60 self.drop_record = saved_drop_record.clone();
61 self.dfs_safedrop(child, path, fn_map)?;
62 }
63
64 path.pop();
65 Ok(())
66 }
67
68 pub fn drop_check(&mut self, bb_idx: usize) {
70 let is_cleanup = self.alias_graph.cfg_block(bb_idx).is_cleanup;
71 if let Some(terminator) = self.alias_graph.terminator(bb_idx).cloned() {
72 rap_debug!("drop check bb: {}, {:?}", bb_idx, terminator);
73 match terminator.kind {
74 TerminatorKind::Drop {
75 ref place,
76 target: _,
77 unwind: _,
78 replace: _,
79 drop: _,
80 #[cfg(not(rapx_ge_99))]
81 async_fut: _,
82 } => {
83 if !self.drop_heap_item_check(place) {
84 return;
85 }
86 let value_idx = self.alias_graph.projection(place.clone());
87 checks::sync_drop_record(&self.alias_graph, &mut self.drop_record);
88 self.add_to_drop_record(value_idx, bb_idx, is_cleanup);
89 }
90 TerminatorKind::Call {
91 ref func, ref args, ..
92 } => {
93 let Operand::Constant(c) = func else {
94 return;
95 };
96 let ty::FnDef(id, ..) = c.ty().kind() else {
97 return;
98 };
99 if !is_drop_fn(*id) {
100 return;
101 }
102 if !args.is_empty() {
103 let place = match args[0].node {
104 Operand::Copy(place) => place,
105 Operand::Move(place) => place,
106 _ => {
107 rap_error!("Constant operand exists: {:?}", args[0]);
108 return;
109 }
110 };
111 if !self.drop_heap_item_check(&place) {
112 return;
113 }
114 let local = self.alias_graph.projection(place.clone());
115 checks::sync_drop_record(&self.alias_graph, &mut self.drop_record);
116 self.add_to_drop_record(local, bb_idx, is_cleanup);
117 }
118 }
119 _ => {}
120 }
121 }
122 }
123
124 pub fn drop_heap_item_check(&self, place: &Place<'tcx>) -> bool {
125 let tcx = self.alias_graph.tcx();
126 let place_ty = place.ty(
127 &tcx.optimized_mir(self.alias_graph.def_id()).local_decls,
128 tcx,
129 );
130 match place_ty.ty.kind() {
131 ty::TyKind::Adt(adtdef, ..) => match self.adt_owner.get(&adtdef.did()) {
132 None => true,
133 Some(owenr_unit) => {
134 let idx = match place_ty.variant_index {
135 Some(vdx) => vdx.index(),
136 None => 0,
137 };
138 if owenr_unit[idx].0.is_onheap() || owenr_unit[idx].1.contains(&true) {
139 true
140 } else {
141 false
142 }
143 }
144 },
145 _ => true,
146 }
147 }
148
149 pub fn process_function_paths_opt(
150 &mut self,
151 precomputed_paths: Option<PathTree>,
152 fn_map: &MopFnAliasMap,
153 ) {
154 self.alias_graph.init_pts_graph();
155 let paths = precomputed_paths.unwrap_or_else(|| self.alias_graph.enumerate_paths());
156 let Some(root) = paths.root() else { return };
157 let mut path = Vec::new();
158 let _ = self.dfs_safedrop(root, &mut path, fn_map);
159 }
160 pub fn report_bugs(&self) {
161 rap_debug!(
162 "report bugs, id: {:?}, uaf: {:?}",
163 self.alias_graph.def_id(),
164 self.bug_records.uaf_bugs
165 );
166 let filename = get_filename(self.alias_graph.tcx(), self.alias_graph.def_id());
167 match filename {
168 Some(filename) => {
169 if filename.contains(".cargo") {
170 return;
171 }
172 }
173 None => {}
174 }
175 if self.bug_records.is_bug_free() {
176 return;
177 }
178 let fn_name = match get_name(self.alias_graph.tcx(), self.alias_graph.def_id()) {
179 Some(name) => name,
180 None => Symbol::intern("no symbol available"),
181 };
182 let body = self
183 .alias_graph
184 .tcx()
185 .optimized_mir(self.alias_graph.def_id());
186 self.bug_records
187 .df_bugs_output(body, fn_name, self.alias_graph.span());
188 self.bug_records
189 .uaf_bugs_output(body, fn_name, self.alias_graph.span());
190 self.bug_records
191 .dp_bug_output(body, fn_name, self.alias_graph.span());
192 }
193
194 pub fn df_check(
195 &mut self,
196 value_idx: usize,
197 bb_idx: usize,
198 span: Span,
199 flag_cleanup: bool,
200 ) -> bool {
201 let local = self.alias_graph.values[value_idx].local;
202 rap_debug!(
203 "df_check: value_idx = {:?}, bb_idx = {:?}",
204 value_idx,
205 bb_idx,
206 );
207 let Some(confidence) = checks::check_drop_status(&self.alias_graph, &mut self.drop_record, value_idx) else {
208 return false;
209 };
210
211 for item in &self.drop_record {
212 rap_debug!("drop_spot: {:?}", item);
213 }
214
215 let drop_spot = self.drop_record[value_idx].drop_spot;
216 let result_type = self
217 .bug_records
218 .try_merge_pair(drop_spot, bb_idx, BugType::DoubleFree);
219 let Some(t) = result_type else {
220 return true;
221 };
222
223 let bug = checks::make_bug(
224 &self.drop_record[value_idx],
225 LocalSpot::new(bb_idx, local),
226 span.clone(),
227 confidence,
228 t,
229 );
230 let target_map = if flag_cleanup {
231 &mut self.bug_records.df_bugs_unwind
232 } else {
233 &mut self.bug_records.df_bugs
234 };
235 if !target_map.contains_key(&local) {
236 target_map.insert(local, bug);
237 if flag_cleanup {
238 rap_info!(
239 "Find a double free bug {} during unwinding; add to records.",
240 local
241 );
242 } else {
243 rap_info!("Find a double free bug {}; add to records.", local);
244 }
245 }
246 true
247 }
248
249 pub fn dp_check(&mut self, flag_cleanup: bool) {
250 rap_debug!("dangling pointer check");
251 if flag_cleanup {
252 for arg_idx in 1..self.alias_graph.arg_size() + 1 {
253 self.dp_check_arg(arg_idx, flag_cleanup);
254 }
255 } else if self.alias_graph.value_may_drop(0)
256 && (self.drop_record[0].is_dropped || self.drop_record[0].has_dropped_field)
257 {
258 let Some(confidence) = checks::check_drop_status(&self.alias_graph, &mut self.drop_record, 0) else {
259 return;
260 };
261 if !self.bug_records.dp_bugs.contains_key(&0) {
262 let bug = checks::make_bug(
263 &self.drop_record[0],
264 LocalSpot::from_local(0),
265 self.alias_graph.span().clone(),
266 confidence,
267 BugType::DanglingPointer,
268 );
269 self.bug_records.dp_bugs.insert(0, bug);
270 rap_info!("Find a dangling pointer 0; add to record.");
271 }
272 } else {
273 for arg_idx in 0..self.alias_graph.arg_size() + 1 {
274 self.dp_check_arg(arg_idx, false);
275 }
276 }
277 }
278
279 fn dp_check_arg(&mut self, arg_idx: usize, flag_cleanup: bool) {
280 if !self.alias_graph.value_is_ptr(arg_idx) {
281 return;
282 }
283 let Some(confidence) = checks::check_drop_status(&self.alias_graph, &mut self.drop_record, arg_idx) else {
284 return;
285 };
286 let bug = checks::make_bug(
287 &self.drop_record[arg_idx],
288 LocalSpot::from_local(arg_idx),
289 self.alias_graph.span().clone(),
290 confidence,
291 BugType::DanglingPointer,
292 );
293 if flag_cleanup {
294 if !self.bug_records.dp_bugs_unwind.contains_key(&arg_idx) {
295 let drop_spot = self.drop_record[arg_idx].drop_spot;
296 if self
297 .bug_records
298 .dp_bugs_unwind
299 .values()
300 .any(|e| e.drop_spot == drop_spot)
301 {
302 return;
303 }
304 self.bug_records.dp_bugs_unwind.insert(arg_idx, bug);
305 rap_info!(
306 "Find a dangling pointer {} during unwinding; add to record.",
307 arg_idx
308 );
309 }
310 } else if !self.bug_records.dp_bugs.contains_key(&arg_idx) {
311 let drop_spot = self.drop_record[arg_idx].drop_spot;
312 if self
313 .bug_records
314 .dp_bugs
315 .values()
316 .any(|e| e.drop_spot == drop_spot)
317 {
318 return;
319 }
320 self.bug_records.dp_bugs.insert(arg_idx, bug);
321 rap_info!("Find a dangling pointer {}; add to record.", arg_idx);
322 }
323 }
324}