1use super::{bug_records::*, corner_case::*, drop::*, graph::*};
2use crate::{
3 analysis::core::alias_analysis::default::{MopFnAliasMap, block::Term, types::ValueKind},
4 utils::source::{get_filename, get_name},
5};
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_middle::{
8 mir::{
9 Operand::{self, Constant, Copy, Move},
10 Place, TerminatorKind,
11 },
12 ty::{self, TypingEnv},
13};
14use rustc_span::{Span, Symbol};
15
16pub const VISIT_LIMIT: usize = 1000;
17
18impl<'tcx> SafeDropGraph<'tcx> {
19 pub fn drop_check(&mut self, bb_idx: usize) {
21 let cur_block = self.mop_graph.blocks[bb_idx].clone();
22 let is_cleanup = cur_block.is_cleanup;
23 if let Term::Drop(drop) = cur_block.terminator {
24 rap_debug!("drop check bb: {}, {:?}", bb_idx, drop);
25 match drop.kind {
26 TerminatorKind::Drop {
27 ref place,
28 target: _,
29 unwind: _,
30 replace: _,
31 drop: _,
32 async_fut: _,
33 } => {
34 if !self.drop_heap_item_check(place) {
35 return;
36 }
37 let value_idx = self.projection(place.clone());
38 let info = drop.source_info.clone();
39 self.add_to_drop_record(value_idx, bb_idx, &info, is_cleanup);
40 }
41 TerminatorKind::Call {
42 func: _, ref args, ..
43 } => {
44 if args.len() > 0 {
45 let place = match args[0].node {
46 Operand::Copy(place) => place,
47 Operand::Move(place) => place,
48 _ => {
49 rap_error!("Constant operand exists: {:?}", args[0]);
50 return;
51 }
52 };
53 if !self.drop_heap_item_check(&place) {
54 return;
55 }
56 let local = self.projection(place.clone());
57 let info = drop.source_info.clone();
58 self.add_to_drop_record(local, bb_idx, &info, is_cleanup);
59 }
60 }
61 _ => {}
62 }
63 }
64 }
65
66 pub fn drop_heap_item_check(&self, place: &Place<'tcx>) -> bool {
67 let tcx = self.mop_graph.tcx;
68 let place_ty = place.ty(&tcx.optimized_mir(self.mop_graph.def_id).local_decls, tcx);
69 match place_ty.ty.kind() {
70 ty::TyKind::Adt(adtdef, ..) => match self.adt_owner.get(&adtdef.did()) {
71 None => true,
72 Some(owenr_unit) => {
73 let idx = match place_ty.variant_index {
74 Some(vdx) => vdx.index(),
75 None => 0,
76 };
77 if owenr_unit[idx].0.is_onheap() || owenr_unit[idx].1.contains(&true) {
78 true
79 } else {
80 false
81 }
82 }
83 },
84 _ => true,
85 }
86 }
87
88 pub fn split_check(&mut self, bb_idx: usize, fn_map: &MopFnAliasMap) {
89 let backup_values = self.mop_graph.values.clone(); let backup_constant = self.mop_graph.constants.clone();
92 let backup_alias_sets = self.mop_graph.alias_sets.clone();
93 let backup_drop_record = self.drop_record.clone();
94 self.check(bb_idx, fn_map);
95 self.mop_graph.values = backup_values;
97 self.mop_graph.constants = backup_constant;
98 self.mop_graph.alias_sets = backup_alias_sets;
99 self.drop_record = backup_drop_record;
100 }
101
102 pub fn split_check_with_cond(
103 &mut self,
104 bb_idx: usize,
105 path_discr_id: usize,
106 path_discr_val: usize,
107 fn_map: &MopFnAliasMap,
108 ) {
109 let backup_values = self.mop_graph.values.clone(); let backup_constant = self.mop_graph.constants.clone();
112 let backup_alias_sets = self.mop_graph.alias_sets.clone();
113 let backup_drop_record = self.drop_record.clone();
114 self.mop_graph
116 .constants
117 .insert(path_discr_id, path_discr_val);
118 self.check(bb_idx, fn_map);
119 self.mop_graph.values = backup_values;
121 self.mop_graph.constants = backup_constant;
122 self.mop_graph.alias_sets = backup_alias_sets;
123 self.drop_record = backup_drop_record;
124 }
125
126 pub fn check(&mut self, bb_idx: usize, fn_map: &MopFnAliasMap) {
128 self.mop_graph.visit_times += 1;
129 if self.mop_graph.visit_times > VISIT_LIMIT {
130 return;
131 }
132 let scc_idx = self.mop_graph.blocks[bb_idx].scc.enter;
133 let cur_block = self.mop_graph.blocks[bb_idx].clone();
134 rap_debug!(
135 "Checking bb: {}, scc_idx: {}, scc: {:?}",
136 bb_idx,
137 scc_idx,
138 cur_block.scc.clone(),
139 );
140
141 if bb_idx == scc_idx && !cur_block.scc.nodes.is_empty() {
142 rap_debug!("check {:?} as a scc", bb_idx);
143 self.check_scc(bb_idx, fn_map);
144 } else {
145 self.check_single_node(bb_idx, fn_map);
146 self.handle_nexts(bb_idx, fn_map, None, None);
147 }
148 }
149
150 pub fn check_scc(&mut self, bb_idx: usize, fn_map: &MopFnAliasMap) {
151 let cur_block = self.mop_graph.blocks[bb_idx].clone();
152 let scc_tree = self.mop_graph.sort_scc_tree(&cur_block.scc);
154 let paths_in_scc =
155 self.mop_graph
156 .find_scc_paths(bb_idx, &scc_tree, &mut FxHashMap::default());
157 rap_debug!("Paths in scc: {:?}", paths_in_scc);
158
159 let backup_values = self.mop_graph.values.clone(); let backup_constant = self.mop_graph.constants.clone();
161 let backup_alias_sets = self.mop_graph.alias_sets.clone();
162 let backup_drop_record = self.drop_record.clone();
163 for raw_path in &paths_in_scc {
164 let path = &raw_path.0;
165 let path_constants = &raw_path.1;
166
167 if !path.is_empty() {
168 for idx in &path[..path.len() - 1] {
169 self.alias_bb(*idx);
170 self.alias_bbcall(*idx, fn_map);
171 self.drop_check(*idx);
172 }
173 }
174 if let Some(&last_node) = path.last() {
176 if self.mop_graph.blocks[last_node].scc.nodes.is_empty() {
177 self.check_single_node(last_node, fn_map);
178 self.handle_nexts(last_node, fn_map, None, Some(path_constants));
179 } else {
180 }
182 }
183 self.mop_graph.alias_sets = backup_alias_sets.clone();
184 self.mop_graph.values = backup_values.clone();
185 self.mop_graph.constants = backup_constant.clone();
186 self.drop_record = backup_drop_record.clone();
187 }
188 }
189
190 pub fn check_single_node(&mut self, bb_idx: usize, fn_map: &MopFnAliasMap) {
191 let cur_block = self.mop_graph.blocks[bb_idx].clone();
192 rap_debug!("check {:?} as a node", bb_idx);
193 self.alias_bb(bb_idx);
194 self.alias_bbcall(bb_idx, fn_map);
195 self.drop_check(bb_idx);
196
197 if cur_block.next.is_empty() {
200 if should_check(self.mop_graph.def_id) {
201 self.dp_check(cur_block.is_cleanup);
202 }
203 }
204 }
205
206 pub fn handle_nexts(
207 &mut self,
208 bb_idx: usize,
209 fn_map: &MopFnAliasMap,
210 exclusive_nodes: Option<&FxHashSet<usize>>,
211 path_constraints: Option<&FxHashMap<usize, usize>>,
212 ) {
213 let cur_block = self.mop_graph.blocks[bb_idx].clone();
214 let tcx = self.mop_graph.tcx;
215
216 if let Some(path_constants) = path_constraints {
218 self.mop_graph.constants.extend(path_constants);
219 }
220
221 let mut single_target = false;
223 let mut sw_val = 0;
224 let mut sw_target = 0; let mut path_discr_id = 0; let mut sw_targets = None; if let Term::Switch(switch) = &cur_block.terminator {
228 rap_debug!("Handle switchInt in bb {:?}", cur_block);
229 if let TerminatorKind::SwitchInt {
230 ref discr,
231 ref targets,
232 } = switch.kind
233 {
234 rap_debug!("{:?}", switch);
235 rap_debug!("{:?}", self.mop_graph.constants);
236 match discr {
237 Copy(p) | Move(p) => {
238 let value_idx = self.projection(*p);
239 let local_decls = &tcx.optimized_mir(self.mop_graph.def_id).local_decls;
240 let place_ty = (*p).ty(local_decls, tcx);
241 rap_debug!("value_idx: {:?}", value_idx);
242 match place_ty.ty.kind() {
243 ty::TyKind::Bool => {
244 rap_debug!("SwitchInt via Bool");
245 if let Some(constant) = self.mop_graph.constants.get(&value_idx) {
246 if *constant != usize::MAX {
247 single_target = true;
248 sw_val = *constant;
249 }
250 }
251 path_discr_id = value_idx;
252 sw_targets = Some(targets.clone());
253 }
254 _ => {
255 if let Some(father) = self
256 .mop_graph
257 .discriminants
258 .get(&self.mop_graph.values[value_idx].local)
259 {
260 if let Some(constant) = self.mop_graph.constants.get(father) {
261 if *constant != usize::MAX {
262 single_target = true;
263 sw_val = *constant;
264 }
265 }
266 if self.mop_graph.values[value_idx].local == value_idx {
267 path_discr_id = *father;
268 sw_targets = Some(targets.clone());
269 }
270 }
271 }
272 }
273 }
274 Constant(c) => {
275 single_target = true;
276 let ty_env = TypingEnv::post_analysis(tcx, self.mop_graph.def_id);
277 if let Some(val) = c.const_.try_eval_target_usize(tcx, ty_env) {
278 sw_val = val as usize;
279 }
280 }
281 }
282 if single_target {
283 for iter in targets.iter() {
288 if iter.0 as usize == sw_val {
289 sw_target = iter.1.as_usize();
290 break;
291 }
292 }
293 if sw_target == 0 {
298 let all_target = targets.all_targets();
299 sw_target = all_target[all_target.len() - 1].as_usize();
300 }
301 }
302 }
303 }
304 if single_target {
307 match exclusive_nodes {
308 Some(exclusive) => {
309 if !exclusive.contains(&sw_target) {
310 self.check(sw_target, fn_map);
311 }
312 }
313 None => {
314 self.check(sw_target, fn_map);
315 }
316 }
317 } else {
318 if let Some(targets) = sw_targets {
320 for iter in targets.iter() {
321 if self.mop_graph.visit_times > VISIT_LIMIT {
322 continue;
323 }
324 let next = iter.1.as_usize();
325
326 match exclusive_nodes {
327 Some(exclusive) => {
328 if exclusive.contains(&next) {
329 continue;
330 }
331 }
332 None => {}
333 }
334 let path_discr_val = iter.0 as usize;
335 self.split_check_with_cond(next, path_discr_id, path_discr_val, fn_map);
336 }
337 let all_targets = targets.all_targets();
338 let next_idx = all_targets[all_targets.len() - 1].as_usize();
339 let path_discr_val = usize::MAX; self.split_check_with_cond(next_idx, path_discr_id, path_discr_val, fn_map);
341 } else {
342 for next in &cur_block.next {
343 if self.mop_graph.visit_times > VISIT_LIMIT {
344 continue;
345 }
346
347 match exclusive_nodes {
348 Some(exclusive) => {
349 if exclusive.contains(&next) {
350 continue;
351 }
352 }
353 None => {}
354 }
355 self.split_check(*next, fn_map);
356 }
357 }
358 }
359 }
360 pub fn report_bugs(&self) {
361 rap_debug!(
362 "report bugs, id: {:?}, uaf: {:?}",
363 self.mop_graph.def_id,
364 self.bug_records.uaf_bugs
365 );
366 let filename = get_filename(self.mop_graph.tcx, self.mop_graph.def_id);
367 match filename {
368 Some(filename) => {
369 if filename.contains(".cargo") {
370 return;
371 }
372 }
373 None => {}
374 }
375 if self.bug_records.is_bug_free() {
376 return;
377 }
378 let fn_name = match get_name(self.mop_graph.tcx, self.mop_graph.def_id) {
379 Some(name) => name,
380 None => Symbol::intern("no symbol available"),
381 };
382 let body = self.mop_graph.tcx.optimized_mir(self.mop_graph.def_id);
383 self.bug_records
384 .df_bugs_output(body, fn_name, self.mop_graph.span);
385 self.bug_records
386 .uaf_bugs_output(body, fn_name, self.mop_graph.span);
387 self.bug_records
388 .dp_bug_output(body, fn_name, self.mop_graph.span);
389 }
397
398 pub fn uaf_check(&mut self, value_idx: usize, bb_idx: usize, span: Span, is_fncall: bool) {
399 let local = self.mop_graph.values[value_idx].local;
400 rap_debug!(
401 "uaf_check, idx: {:?}, local: {:?}, drop_record: {:?}",
402 value_idx,
403 local,
404 self.drop_record[value_idx],
405 );
406
407 if !self.mop_graph.values[value_idx].may_drop {
408 return;
409 }
410 if self.mop_graph.values[value_idx].is_ptr() && !is_fncall {
411 return;
412 }
413
414 self.fetch_drop_info(value_idx);
415
416 let mut fully_dropped = true;
417 if !self.drop_record[value_idx].is_dropped {
418 fully_dropped = false;
419 if !self.drop_record[value_idx].has_dropped_field {
420 return;
421 }
422 }
423
424 let kind = self.mop_graph.values[value_idx].kind;
425 let confidence = Self::rate_confidence(kind, fully_dropped);
426
427 let bug = TyBug {
428 drop_spot: self.drop_record[value_idx].drop_spot,
429 trigger_info: LocalSpot::new(bb_idx, local),
430 prop_chain: self.drop_record[value_idx].prop_chain.clone(),
431 span: span.clone(),
432 confidence,
433 };
434 if self.bug_records.uaf_bugs.contains_key(&local) {
435 return;
436 }
437 rap_warn!("Find a use-after-free bug {:?}; add to records", bug);
438 self.bug_records.uaf_bugs.insert(local, bug);
439 }
440
441 pub fn rate_confidence(kind: ValueKind, fully_dropped: bool) -> usize {
442 let confidence = match (kind, fully_dropped) {
443 (ValueKind::SpecialPtr, _) => 0,
444 (_, true) => 99,
445 (_, false) => 50,
446 };
447 confidence
448 }
449
450 pub fn df_check(
451 &mut self,
452 value_idx: usize,
453 bb_idx: usize,
454 span: Span,
455 flag_cleanup: bool,
456 ) -> bool {
457 let local = self.mop_graph.values[value_idx].local;
458 rap_debug!(
460 "df_check: value_idx = {:?}, bb_idx = {:?}, alias_sets: {:?}",
461 value_idx,
462 bb_idx,
463 self.mop_graph.alias_sets,
464 );
465 self.fetch_drop_info(value_idx);
466 let mut fully_dropped = true;
467 if !self.drop_record[value_idx].is_dropped {
468 fully_dropped = false;
469 if !self.drop_record[value_idx].has_dropped_field {
470 return false;
471 }
472 }
473 let kind = self.mop_graph.values[value_idx].kind;
474 let confidence = Self::rate_confidence(kind, fully_dropped);
475
476 let bug = TyBug {
477 drop_spot: self.drop_record[value_idx].drop_spot,
478 trigger_info: LocalSpot::new(bb_idx, local),
479 prop_chain: self.drop_record[value_idx].prop_chain.clone(),
480 span: span.clone(),
481 confidence,
482 };
483
484 for item in &self.drop_record {
485 rap_debug!("drop_spot: {:?}", item);
486 }
487 if flag_cleanup {
488 if !self.bug_records.df_bugs_unwind.contains_key(&local) {
489 self.bug_records.df_bugs_unwind.insert(local, bug);
490 rap_info!(
491 "Find a double free bug {} during unwinding; add to records.",
492 local
493 );
494 }
495 } else {
496 if !self.bug_records.df_bugs.contains_key(&local) {
497 self.bug_records.df_bugs.insert(local, bug);
498 rap_info!("Find a double free bug {}; add to records.", local);
499 }
500 }
501 return true;
502 }
503
504 pub fn dp_check(&mut self, flag_cleanup: bool) {
505 rap_debug!("dangling pointer check");
506 rap_debug!("current alias sets: {:?}", self.mop_graph.alias_sets);
507 if flag_cleanup {
508 for arg_idx in 1..self.mop_graph.arg_size + 1 {
509 if !self.mop_graph.values[arg_idx].is_ptr() {
510 continue;
511 }
512 self.fetch_drop_info(arg_idx);
513 let mut fully_dropped = true;
514 if !self.drop_record[arg_idx].is_dropped {
515 fully_dropped = false;
516 if !self.drop_record[arg_idx].has_dropped_field {
517 continue;
518 }
519 }
520 let kind = self.mop_graph.values[arg_idx].kind;
521 let confidence = Self::rate_confidence(kind, fully_dropped);
522 let bug = TyBug {
523 drop_spot: self.drop_record[arg_idx].drop_spot,
524 trigger_info: LocalSpot::from_local(arg_idx),
525 prop_chain: self.drop_record[arg_idx].prop_chain.clone(),
526 span: self.mop_graph.span.clone(),
527 confidence,
528 };
529 self.bug_records.dp_bugs_unwind.insert(arg_idx, bug);
530 rap_info!(
531 "Find a dangling pointer {} during unwinding; add to record.",
532 arg_idx
533 );
534 }
535 } else {
536 if self.mop_graph.values[0].may_drop
537 && (self.drop_record[0].is_dropped || self.drop_record[0].has_dropped_field)
538 {
539 self.fetch_drop_info(0);
540 let mut fully_dropped = true;
541 if !self.drop_record[0].is_dropped {
542 fully_dropped = false;
543 }
544
545 let kind = self.mop_graph.values[0].kind;
546 let confidence = Self::rate_confidence(kind, fully_dropped);
547 let bug = TyBug {
548 drop_spot: self.drop_record[0].drop_spot,
549 trigger_info: LocalSpot::from_local(0),
550 prop_chain: self.drop_record[0].prop_chain.clone(),
551 span: self.mop_graph.span.clone(),
552 confidence,
553 };
554 self.bug_records.dp_bugs.insert(0, bug);
555 rap_info!("Find a dangling pointer 0; add to record.");
556 } else {
557 for arg_idx in 0..self.mop_graph.arg_size + 1 {
558 if !self.mop_graph.values[arg_idx].is_ptr() {
559 continue;
560 }
561 self.fetch_drop_info(arg_idx);
562 let mut fully_dropped = true;
563 if !self.drop_record[arg_idx].is_dropped {
564 fully_dropped = false;
565 if !self.drop_record[arg_idx].has_dropped_field {
566 continue;
567 }
568 }
569 let kind = self.mop_graph.values[arg_idx].kind;
570 let confidence = Self::rate_confidence(kind, fully_dropped);
571 let bug = TyBug {
572 drop_spot: self.drop_record[arg_idx].drop_spot,
573 trigger_info: LocalSpot::from_local(arg_idx),
574 prop_chain: self.drop_record[arg_idx].prop_chain.clone(),
575 span: self.mop_graph.span.clone(),
576 confidence,
577 };
578 self.bug_records.dp_bugs.insert(arg_idx, bug);
579 rap_info!("Find a dangling pointer {}; add to record.", arg_idx);
580 }
581 }
582 }
583 }
584}