rapx/analysis/safedrop/
check_bugs.rs

1use super::{bug_records::TyBug, graph::*};
2use crate::{
3    analysis::{
4        core::alias_analysis::default::{types::TyKind, value::*},
5        utils::fn_info::{convert_alias_to_sets, generate_mir_cfg_dot},
6    },
7    utils::source::*,
8};
9use rustc_middle::mir::SourceInfo;
10use rustc_span::{Span, symbol::Symbol};
11
12impl<'tcx> SafeDropGraph<'tcx> {
13    pub fn report_bugs(&self) {
14        rap_debug!(
15            "report bugs, id: {:?}, uaf: {:?}",
16            self.mop_graph.def_id,
17            self.bug_records.uaf_bugs
18        );
19        let filename = get_filename(self.mop_graph.tcx, self.mop_graph.def_id);
20        match filename {
21            Some(filename) => {
22                if filename.contains(".cargo") {
23                    return;
24                }
25            }
26            None => {}
27        }
28        if self.bug_records.is_bug_free() {
29            return;
30        }
31        let fn_name = match get_name(self.mop_graph.tcx, self.mop_graph.def_id) {
32            Some(name) => name,
33            None => Symbol::intern("no symbol available"),
34        };
35        let body = self.mop_graph.tcx.optimized_mir(self.mop_graph.def_id);
36        self.bug_records
37            .df_bugs_output(body, fn_name, self.mop_graph.span);
38        self.bug_records
39            .uaf_bugs_output(body, fn_name, self.mop_graph.span);
40        self.bug_records
41            .dp_bug_output(body, fn_name, self.mop_graph.span);
42        let _ = generate_mir_cfg_dot(
43            self.mop_graph.tcx,
44            self.mop_graph.def_id,
45            &self.mop_graph.alias_set,
46        );
47        rap_debug!(
48            "Alias: {:?}",
49            convert_alias_to_sets(self.mop_graph.alias_set.clone())
50        );
51    }
52
53    pub fn uaf_check(&mut self, bb_idx: usize, idx: usize, span: Span, is_fncall: bool) {
54        let local = self.mop_graph.values[idx].local;
55        if !self.mop_graph.values[idx].may_drop {
56            return;
57        }
58        rap_debug!(
59            "uaf_check, idx: {:?}, local: {:?}, drop_record: {:?}, local_drop_record: {:?}",
60            idx,
61            local,
62            self.drop_record[idx],
63            self.drop_record[local],
64        );
65
66        self.sync_drop_record(idx);
67        rap_debug!(
68            "after sync drop record: idx: {:?}, local: {:?}, drop_record: {:?}",
69            idx,
70            local,
71            self.drop_record[idx]
72        );
73        if !self.drop_record[idx].is_dropped {
74            return;
75        }
76        rap_debug!(
77            "is_ptr: {}, is_fn_call: {}",
78            self.mop_graph.values[idx].is_ptr(),
79            is_fncall
80        );
81        if self.mop_graph.values[idx].is_ptr() && !is_fncall {
82            return;
83        }
84
85        let confidence = match self.mop_graph.values[idx].kind {
86            TyKind::CornerCase => 0,
87            _ => 99,
88        };
89
90        let bug = TyBug {
91            drop_bb: self.drop_record[idx].drop_at_bb,
92            drop_id: self.drop_record[idx].drop_via_local,
93            trigger_bb: bb_idx,
94            trigger_id: local,
95            span: span.clone(),
96            confidence,
97        };
98        rap_debug!("Find use-after-free bug {:?}; add to records", bug);
99        if self.bug_records.uaf_bugs.contains_key(&local) {
100            return;
101        }
102        self.bug_records.uaf_bugs.insert(local, bug);
103        rap_debug!("Find use-after-free bug {:?}; add to records", local);
104    }
105
106    pub fn sync_drop_record(&mut self, idx: usize) {
107        if idx >= self.mop_graph.values.len() {
108            return;
109        }
110        let local = self.mop_graph.values[idx].local;
111        if self.drop_record[local].is_dropped {
112            self.drop_record[idx] = self.drop_record[local];
113        }
114        let aliases = self.mop_graph.get_alias_set(local);
115        for i in aliases {
116            if self.drop_record[i].is_dropped {
117                self.drop_record[idx] = self.drop_record[i];
118                return;
119            }
120        }
121    }
122
123    pub fn df_check(&mut self, bb_idx: usize, idx: usize, span: Span, flag_cleanup: bool) -> bool {
124        let local = self.mop_graph.values[idx].local;
125        // If the value has not been dropped, it is not a double free.
126        rap_debug!(
127            "df_check: bb_idx = {:?}, idx = {:?}, local = {:?}",
128            bb_idx,
129            idx,
130            local
131        );
132        rap_debug!(
133            "df_check: is alive? {:?}",
134            self.mop_graph.values[idx].is_alive()
135        );
136        if self.mop_graph.values[idx].is_alive() {
137            return false;
138        }
139        let confidence = match self.mop_graph.values[idx].kind {
140            TyKind::CornerCase => 0,
141            _ => 99,
142        };
143        let bug = TyBug {
144            drop_bb: self.drop_record[idx].drop_at_bb,
145            drop_id: self.drop_record[idx].drop_via_local,
146            trigger_bb: bb_idx,
147            trigger_id: local,
148            span: span.clone(),
149            confidence,
150        };
151
152        if flag_cleanup {
153            if !self.bug_records.df_bugs_unwind.contains_key(&local) {
154                self.bug_records.df_bugs_unwind.insert(local, bug);
155                rap_debug!(
156                    "Find double free bug {} during unwinding; add to records.",
157                    local
158                );
159            }
160        } else {
161            if !self.bug_records.df_bugs.contains_key(&local) {
162                self.bug_records.df_bugs.insert(local, bug);
163                rap_debug!("Find double free bug {}; add to records.", local);
164            }
165        }
166        return true;
167    }
168
169    pub fn dp_check(&mut self, flag_cleanup: bool) {
170        if flag_cleanup {
171            for arg_idx in 1..self.mop_graph.arg_size + 1 {
172                if self.mop_graph.values[arg_idx].is_ptr() && self.drop_record[arg_idx].is_dropped {
173                    let confidence = match self.mop_graph.values[arg_idx].kind {
174                        TyKind::CornerCase => 0,
175                        _ => 99,
176                    };
177                    let bug = TyBug {
178                        drop_bb: self.drop_record[arg_idx].drop_at_bb,
179                        drop_id: self.drop_record[arg_idx].drop_via_local,
180                        trigger_bb: usize::MAX,
181                        trigger_id: arg_idx,
182                        span: self.mop_graph.span.clone(),
183                        confidence,
184                    };
185                    self.bug_records.dp_bugs_unwind.insert(arg_idx, bug);
186                    rap_debug!(
187                        "Find dangling pointer {} during unwinding; add to record.",
188                        arg_idx
189                    );
190                }
191            }
192        } else {
193            if self.mop_graph.values[0].may_drop && self.drop_record[0].is_dropped {
194                let confidence = match self.mop_graph.values[0].kind {
195                    TyKind::CornerCase => 0,
196                    _ => 99,
197                };
198                let bug = TyBug {
199                    drop_bb: self.drop_record[0].drop_at_bb,
200                    drop_id: self.drop_record[0].drop_via_local,
201                    trigger_bb: usize::MAX,
202                    trigger_id: 0,
203                    span: self.mop_graph.span.clone(),
204                    confidence,
205                };
206                self.bug_records.dp_bugs.insert(0, bug);
207                rap_debug!("Find dangling pointer 0; add to record.");
208            } else {
209                for arg_idx in 0..self.mop_graph.arg_size + 1 {
210                    if self.mop_graph.values[arg_idx].is_ptr()
211                        && self.drop_record[arg_idx].is_dropped
212                    {
213                        let confidence = match self.mop_graph.values[arg_idx].kind {
214                            TyKind::CornerCase => 0,
215                            _ => 99,
216                        };
217                        let bug = TyBug {
218                            drop_bb: self.drop_record[arg_idx].drop_at_bb,
219                            drop_id: self.drop_record[arg_idx].drop_via_local,
220                            trigger_bb: usize::MAX,
221                            trigger_id: arg_idx,
222                            span: self.mop_graph.span.clone(),
223                            confidence,
224                        };
225                        self.bug_records.dp_bugs.insert(arg_idx, bug);
226                        rap_debug!("Find dangling pointer {}; add to record.", arg_idx);
227                    }
228                }
229            }
230        }
231    }
232
233    /*
234     * Mark the node as dropped.
235     * flag_cleanup: used to distinguish if a bug occurs in the unwinding path.
236     */
237    pub fn add_to_drop_record(
238        &mut self,
239        idx: usize,     // the value to be dropped
240        via_idx: usize, // the value is dropped via its alias: via_idx
241        birth: usize,
242        info: &SourceInfo,
243        flag_inprocess: bool,
244        bb_idx: usize,
245        flag_cleanup: bool,
246    ) {
247        //Rc drop
248        if self.mop_graph.values[idx].is_corner_case() {
249            return;
250        }
251        //check if there is a double free bug.
252        if !flag_inprocess && self.df_check(bb_idx, idx, self.mop_graph.span, flag_cleanup) {
253            return;
254        }
255        if !self.drop_record[idx].is_dropped {
256            self.drop_record[idx] =
257                DropRecord::new(true, bb_idx, self.mop_graph.values[via_idx].local);
258            rap_debug!(
259                "add_to_drop_record: idx = {}, {:?}",
260                idx,
261                self.drop_record[idx]
262            );
263            //drop their alias
264            for i in 0..self.mop_graph.values.len() {
265                if !self.mop_graph.union_is_same(idx, i)
266                    || i == idx
267                    || self.mop_graph.values[i].is_ref()
268                {
269                    continue;
270                }
271                self.add_to_drop_record(
272                    i,
273                    self.mop_graph.values[via_idx].local,
274                    birth,
275                    info,
276                    true,
277                    bb_idx,
278                    flag_cleanup,
279                );
280            }
281        } else {
282            return;
283        }
284        //drop the fields of the root node.
285        //alias flag is used to avoid the fields of the alias are dropped repeatly.
286        if !flag_inprocess {
287            for (_, field_idx) in self.mop_graph.values[idx].fields.clone() {
288                if self.mop_graph.values[idx].is_tuple()
289                    && !self.mop_graph.values[field_idx].need_drop
290                {
291                    continue;
292                }
293                self.add_to_drop_record(
294                    field_idx,
295                    self.mop_graph.values[via_idx].local,
296                    birth,
297                    info,
298                    false,
299                    bb_idx,
300                    flag_cleanup,
301                );
302            }
303        }
304        //SCC.
305        if self.mop_graph.values[idx].birth < birth as isize && self.mop_graph.values[idx].may_drop
306        {
307            self.mop_graph.values[idx].drop();
308        }
309    }
310
311    pub fn get_field_seq(&self, value: &Value) -> Vec<usize> {
312        let mut field_id_seq = vec![];
313        let mut node_ref = value;
314        while node_ref.field_id != usize::MAX {
315            field_id_seq.push(node_ref.field_id);
316            node_ref = &self.mop_graph.values[value.father];
317        }
318        return field_id_seq;
319    }
320}