Skip to main content

rapx/check/safedrop/
safedrop.rs

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