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