Skip to main content

rapx/check/safedrop/
safedrop.rs

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    // analyze the drop statement and update the liveness for nodes.
69    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}