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