1use rustc_span::Span;
2use crate::analysis::alias::default::graph::AliasGraph;
3use crate::analysis::alias::default::types::ValueKind;
4use super::bug_records::*;
5use super::drop::*;
6
7pub fn sync_drop_record(graph: &AliasGraph, drop_record: &mut Vec<DropRecord>) {
13 let target_len = graph.values.len();
14 while drop_record.len() < target_len {
15 let new_idx = drop_record.len();
16 let father = if new_idx < graph.values.len() {
17 graph.values[new_idx].father.clone()
18 } else {
19 None
20 };
21 drop_record.push(if let Some(ref fi) = father {
22 DropRecord::from(new_idx, &drop_record[fi.father_value_id])
23 } else {
24 DropRecord::false_record(new_idx)
25 });
26 }
27}
28
29pub fn clear_drop_info(graph: &AliasGraph, drop_record: &mut Vec<DropRecord>, value_idx: usize) {
30 rap_debug!("clear_drop: value_idx = {}", value_idx);
31 drop_record[value_idx].clear();
32 clear_field_drop(graph, drop_record, value_idx);
33 clear_father_drop(graph, drop_record, value_idx);
34}
35
36pub fn uaf_check(
37 graph: &AliasGraph,
38 drop_record: &mut Vec<DropRecord>,
39 bug_records: &mut BugRecords,
40 value_idx: usize,
41 bb_idx: usize,
42 span: Span,
43 is_fncall: bool,
44) {
45 let local = graph.values[value_idx].local;
46 rap_debug!(
47 "uaf_check, idx: {:?}, local: {:?}, drop_record: {:?}",
48 value_idx,
49 local,
50 drop_record[value_idx],
51 );
52 if !graph.value_may_drop(value_idx) {
53 return;
54 }
55 if graph.value_is_ptr(value_idx) && !is_fncall {
56 return;
57 }
58 let Some(confidence) = check_drop_status(graph, drop_record, value_idx) else {
59 return;
60 };
61 if bug_records.uaf_bugs.contains_key(&local) {
62 return;
63 }
64 let drop_spot = drop_record[value_idx].drop_spot;
65 if let Some(t) = bug_records.try_merge_pair(drop_spot, bb_idx, BugType::UseAfterFree) {
66 let bug = make_bug(
67 &drop_record[value_idx],
68 LocalSpot::new(bb_idx, local),
69 span.clone(),
70 confidence,
71 t,
72 );
73 rap_warn!("Find a use-after-free bug {:?}; add to records", bug);
74 bug_records.uaf_bugs.insert(local, bug);
75 }
76}
77
78pub fn check_drop_status(
81 graph: &AliasGraph,
82 drop_record: &mut Vec<DropRecord>,
83 idx: usize,
84) -> Option<usize> {
85 fetch_drop_info(graph, drop_record, idx);
86 let mut fully_dropped = true;
87 if !drop_record[idx].is_dropped {
88 fully_dropped = false;
89 if !drop_record[idx].has_dropped_field {
90 return None;
91 }
92 }
93 let kind = graph.value_to_slot_idx(idx)
94 .map(|si| graph.pts_graph.slot_kind(si))
95 .unwrap_or(ValueKind::Adt);
96 Some(rate_confidence(kind, fully_dropped))
97}
98
99fn rate_confidence(kind: ValueKind, fully_dropped: bool) -> usize {
100 match (kind, fully_dropped) {
101 (ValueKind::SpecialPtr, _) => 0,
102 (_, true) => 99,
103 (_, false) => 50,
104 }
105}
106
107pub fn make_bug(
108 drop_record: &DropRecord,
109 trigger_info: LocalSpot,
110 span: Span,
111 confidence: usize,
112 bug_type: BugType,
113) -> TyBug {
114 TyBug {
115 drop_spot: drop_record.drop_spot,
116 trigger_info,
117 span,
118 confidence,
119 bug_type,
120 }
121}
122
123pub fn push_drop_info(
126 graph: &AliasGraph,
127 drop_record: &mut Vec<DropRecord>,
128 value_idx: usize,
129 drop_spot: LocalSpot,
130) {
131 push_drop_bottom_up(graph, drop_record, value_idx, drop_spot);
132 push_drop_top_down(graph, drop_record, value_idx, drop_spot);
133 push_drop_through_move(graph, drop_record, value_idx, drop_spot);
134}
135
136fn push_drop_through_move(
137 graph: &AliasGraph,
138 drop_record: &mut Vec<DropRecord>,
139 value_idx: usize,
140 drop_spot: LocalSpot,
141) {
142 if let Some(&src) = graph.move_sources.get(&value_idx) {
143 if !drop_record[src].is_dropped {
144 drop_record[src] = DropRecord::new(src, true, drop_spot);
145 }
146 }
147 for (&dest, &src) in graph.move_sources.iter() {
148 if src == value_idx && !drop_record[dest].is_dropped {
149 drop_record[dest] = DropRecord::new(dest, true, drop_spot);
150 }
151 }
152}
153
154fn push_drop_bottom_up(
155 graph: &AliasGraph,
156 drop_record: &mut Vec<DropRecord>,
157 value_idx: usize,
158 drop_spot: LocalSpot,
159) {
160 rap_debug!("push_drop_bottom_up: value_idx = {}", value_idx);
161 let mut father = graph.values[value_idx].father.clone();
162 let mut prop_chain = vec![value_idx];
163 while let Some(father_info) = father {
164 let father_idx = father_info.father_value_id;
165 drop_record[father_idx].has_dropped_field = true;
166 if !drop_record[father_idx].is_dropped {
167 prop_chain.push(father_idx);
168 drop_record[father_idx].prop_chain = prop_chain.clone();
169 drop_record[father_idx].drop_spot = drop_spot;
170 }
171 rap_debug!("{:?}", drop_record[father_idx]);
172 father = graph.values[father_idx].father.clone();
173 }
174}
175
176fn push_drop_top_down(
177 graph: &AliasGraph,
178 drop_record: &mut Vec<DropRecord>,
179 value_idx: usize,
180 drop_spot: LocalSpot,
181) {
182 rap_debug!("push_drop_top_down: value_idx = {}", value_idx);
183 let mut prop_chain = vec![value_idx];
184 for (_field_id, field_value_id) in graph.values[value_idx].fields.clone() {
185 if graph.value_to_slot_idx(field_value_id)
186 .map_or(false, |si| graph.pts_graph.slot_kind(si) == ValueKind::Ref) {
187 continue;
188 }
189 drop_record[field_value_id] = DropRecord::new(field_value_id, true, drop_spot);
190 prop_chain.push(field_value_id);
191 drop_record[field_value_id].prop_chain = prop_chain.clone();
192 rap_debug!("{:?}", drop_record[field_value_id]);
193 push_drop_top_down(graph, drop_record, field_value_id, drop_spot);
194 }
195}
196
197fn fetch_drop_info(
200 graph: &AliasGraph,
201 drop_record: &mut Vec<DropRecord>,
202 value_idx: usize,
203) {
204 fetch_drop_from_bottom(graph, drop_record, value_idx);
205 fetch_drop_from_top(graph, drop_record, value_idx);
206 fetch_drop_from_alias(graph, drop_record, value_idx);
207 fetch_drop_from_pointee(graph, drop_record, value_idx);
208}
209
210fn fetch_drop_from_pointee(
211 graph: &AliasGraph,
212 drop_record: &mut Vec<DropRecord>,
213 value_idx: usize,
214) {
215 if !graph.value_is_ptr(value_idx) {
216 return;
217 }
218 if drop_record[value_idx].is_dropped || drop_record[value_idx].has_dropped_field {
219 return;
220 }
221 let Some(slot_idx) = graph.value_to_slot_idx(value_idx) else {
222 return;
223 };
224 let pointees: Vec<usize> = graph.pts_graph.direct_pointees(slot_idx)
225 .filter_map(|loc| match loc {
226 crate::analysis::points_to::slot::AbstractLoc::Slot(s) => {
227 if s.fields.is_empty() && s.local < graph.values.len() {
228 Some(s.local)
229 } else {
230 for (v, _) in graph.values.iter().enumerate() {
231 if let Some(v_slot) = graph.value_to_slot_idx(v) {
232 if graph.pts_graph.get_slot(v_slot)
233 .is_some_and(|vs| vs.local == s.local && vs.fields == s.fields)
234 {
235 return Some(v);
236 }
237 }
238 }
239 None
240 }
241 }
242 _ => None,
243 })
244 .collect();
245 for &pointee_vidx in &pointees {
246 if pointee_vidx == value_idx {
247 continue;
248 }
249 check_drop_status(graph, drop_record, pointee_vidx);
250 if drop_record[pointee_vidx].is_dropped || drop_record[pointee_vidx].has_dropped_field {
251 drop_record[value_idx] = drop_record[pointee_vidx].clone();
252 drop_record[value_idx].value_index = value_idx;
253 drop_record[value_idx].prop_chain.push(value_idx);
254 break;
255 }
256 }
257}
258
259fn fetch_drop_from_bottom(
260 graph: &AliasGraph,
261 drop_record: &mut Vec<DropRecord>,
262 value_idx: usize,
263) {
264 rap_debug!("fetch_drop_from_bottom: value_idx = {}", value_idx);
265 for (_field_id, field_value_id) in graph.values[value_idx].fields.clone() {
266 rap_debug!("{:?}", drop_record[field_value_id]);
267 fetch_drop_from_alias(graph, drop_record, field_value_id);
268 if drop_record[field_value_id].is_dropped {
269 push_drop_bottom_up(
270 graph,
271 drop_record,
272 field_value_id,
273 drop_record[field_value_id].drop_spot,
274 );
275 rap_debug!("{:?}", drop_record[value_idx]);
276 break;
277 }
278 fetch_drop_from_bottom(graph, drop_record, field_value_id);
279 }
280 if drop_record[value_idx].is_dropped || drop_record[value_idx].has_dropped_field {
281 return;
282 }
283 fetch_drop_from_pts_fields(graph, drop_record, value_idx);
284}
285
286fn fetch_drop_from_pts_fields(
287 graph: &AliasGraph,
288 drop_record: &mut Vec<DropRecord>,
289 value_idx: usize,
290) {
291 let Some(slot_idx) = graph.value_to_slot_idx(value_idx) else {
292 return;
293 };
294 let base_slot = match graph.pts_graph.get_slot(slot_idx) {
295 Some(s) => s.clone(),
296 None => return,
297 };
298 for i in 0..graph.pts_graph.slot_count() {
299 let Some(child_slot) = graph.pts_graph.get_slot(i) else {
300 continue;
301 };
302 if child_slot.local != base_slot.local || child_slot.fields.len() != base_slot.fields.len() + 1 {
303 continue;
304 }
305 if !child_slot.fields.starts_with(&base_slot.fields) {
306 continue;
307 }
308 let mut found = false;
309 for (v, dr) in drop_record.iter().enumerate() {
310 if !dr.is_dropped {
311 continue;
312 }
313 if let Some(drop_slot) = graph.value_to_slot_idx(v) {
314 if graph.pts_graph.may_alias(i, drop_slot) {
315 drop_record[value_idx].has_dropped_field = true;
316 drop_record[value_idx].drop_spot = drop_record[v].drop_spot.clone();
317 found = true;
318 break;
319 }
320 }
321 }
322 if found {
323 break;
324 }
325 }
326}
327
328fn fetch_drop_from_top(
329 graph: &AliasGraph,
330 drop_record: &mut Vec<DropRecord>,
331 value_idx: usize,
332) {
333 rap_debug!("fetch_drop_from_top: value_idx = {}", value_idx);
334 let mut father = graph.values[value_idx].father.clone();
335 while let Some(father_info) = father {
336 let father_idx = father_info.father_value_id;
337 fetch_drop_from_alias(graph, drop_record, father_idx);
338 if drop_record[father_idx].is_dropped {
339 push_drop_top_down(
340 graph,
341 drop_record,
342 father_idx,
343 drop_record[father_idx].drop_spot,
344 );
345 rap_debug!("{:?}", drop_record[value_idx]);
346 break;
347 }
348 father = graph.values[father_idx].father.clone();
349 }
350}
351
352fn fetch_drop_from_alias(
353 graph: &AliasGraph,
354 drop_record: &mut Vec<DropRecord>,
355 value_idx: usize,
356) {
357 rap_debug!("fetch_drop_from_alias: value_idx = {}", value_idx);
358 if let Some(aliases) = get_alias_set(graph, value_idx) {
359 for idx in aliases {
360 if drop_record[idx].is_dropped {
361 drop_record[value_idx] = drop_record[idx].clone();
362 drop_record[value_idx].value_index = value_idx;
363 drop_record[value_idx].prop_chain.push(value_idx);
364 }
365 }
366 }
367}
368
369fn clear_father_drop(
372 graph: &AliasGraph,
373 drop_record: &mut Vec<DropRecord>,
374 value_idx: usize,
375) {
376 rap_debug!("clear_drop_father: value_idx = {}", value_idx);
377 let mut father = graph.values[value_idx].father.clone();
378 while let Some(father_info) = father {
379 let father_idx = father_info.father_value_id;
380 if !drop_record[father_idx].is_dropped {
381 drop_record[father_idx].clear();
382 }
383 father = graph.values[father_idx].father.clone();
384 }
385}
386
387fn clear_field_drop(
388 graph: &AliasGraph,
389 drop_record: &mut Vec<DropRecord>,
390 value_idx: usize,
391) {
392 rap_debug!("clear_field_drop: value_idx = {}", value_idx);
393 for (_field_id, field_value_id) in graph.values[value_idx].fields.clone() {
394 drop_record[field_value_id].clear();
395 clear_field_drop(graph, drop_record, field_value_id);
396 }
397}
398
399fn get_alias_set(graph: &AliasGraph, e: usize) -> Option<Vec<usize>> {
402 graph.get_alias_set(e)
403}