1use crate::analysis::relation_inference::{InferenceRecord, Relation, RelationEdge};
23
24const MIN_SHARED_OWNERS: usize = 2;
25
26pub fn detect_shared(
27 records: &[InferenceRecord],
28 existing_edges: &[RelationEdge],
29) -> Vec<RelationEdge> {
30 let mut relations = Vec::new();
31
32 let mut owners_of: Vec<Vec<usize>> = vec![Vec::new(); records.len()];
34 for edge in existing_edges {
35 if edge.relation == Relation::Owns {
36 owners_of[edge.to].push(edge.from);
37 }
38 }
39
40 for (target_id, owners) in owners_of.iter().enumerate() {
41 if owners.len() < MIN_SHARED_OWNERS {
42 continue;
43 }
44
45 let target = &records[target_id];
46
47 if !looks_like_arc_rc(target) {
48 continue;
49 }
50
51 for i in 0..owners.len() {
52 for j in (i + 1)..owners.len() {
53 relations.push(RelationEdge {
54 from: owners[i],
55 to: owners[j],
56 relation: Relation::Shares,
57 });
58 }
59 }
60 }
61
62 let mut stack_owners: Vec<(usize, usize)> = Vec::new(); for (i, record) in records.iter().enumerate() {
69 if let Some(stack_ptr) = record.stack_ptr {
72 if stack_ptr > 0x1000 {
73 stack_owners.push((i, record.ptr));
75 }
76 }
77 }
78
79 let mut heap_to_records: std::collections::HashMap<usize, Vec<usize>> =
81 std::collections::HashMap::new();
82 for (record_id, heap_ptr) in stack_owners {
83 heap_to_records.entry(heap_ptr).or_default().push(record_id);
84 }
85
86 for (_heap_ptr, record_ids) in heap_to_records {
88 if record_ids.len() >= 2 {
89 for i in 0..record_ids.len() {
90 for j in (i + 1)..record_ids.len() {
91 relations.push(RelationEdge {
93 from: record_ids[i],
94 to: record_ids[j],
95 relation: Relation::ArcClone,
96 });
97 }
98 }
99 }
100 }
101
102 relations
103}
104
105fn looks_like_arc_rc(record: &InferenceRecord) -> bool {
106 if record.size < 16 || record.size > 1024 {
107 return false;
108 }
109
110 let memory = match &record.memory {
111 Some(m) => m,
112 None => return false,
113 };
114
115 if memory.len() < 16 {
116 return false;
117 }
118
119 let strong = memory.read_usize(0).unwrap_or(usize::MAX);
120 let weak = memory.read_usize(8).unwrap_or(usize::MAX);
121
122 let strong_valid = (1..=10000).contains(&strong); let weak_valid = weak <= 1000; strong_valid && weak_valid
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::analysis::unsafe_inference::{OwnedMemoryView, TypeKind};
133
134 fn make_record(
135 id: usize,
136 ptr: usize,
137 size: usize,
138 memory: Option<Vec<u8>>,
139 type_kind: TypeKind,
140 ) -> InferenceRecord {
141 InferenceRecord {
142 id,
143 ptr,
144 size,
145 memory: memory.map(OwnedMemoryView::new),
146 type_kind,
147 confidence: 100,
148 call_stack_hash: None,
149 alloc_time: 0,
150 stack_ptr: None,
151 }
152 }
153
154 fn make_record_with_stack_ptr(
155 id: usize,
156 ptr: usize,
157 stack_ptr: usize,
158 size: usize,
159 memory: Option<Vec<u8>>,
160 type_kind: TypeKind,
161 ) -> InferenceRecord {
162 InferenceRecord {
163 id,
164 ptr,
165 size,
166 memory: memory.map(OwnedMemoryView::new),
167 type_kind,
168 confidence: 100,
169 call_stack_hash: None,
170 alloc_time: 0,
171 stack_ptr: Some(stack_ptr),
172 }
173 }
174
175 #[test]
176 fn test_shared_detection_basic() {
177 let mut arc_inner_data = vec![0u8; 48];
178 arc_inner_data[0..8].copy_from_slice(&2usize.to_le_bytes());
179 arc_inner_data[8..16].copy_from_slice(&0usize.to_le_bytes());
180
181 let records = vec![
182 make_record(0, 0x1000, 24, None, TypeKind::Vec),
183 make_record(1, 0x2000, 24, None, TypeKind::Vec),
184 make_record(2, 0x3000, 48, Some(arc_inner_data), TypeKind::Buffer),
185 ];
186
187 let existing_edges = vec![
188 RelationEdge {
189 from: 0,
190 to: 2,
191 relation: Relation::Owns,
192 },
193 RelationEdge {
194 from: 1,
195 to: 2,
196 relation: Relation::Owns,
197 },
198 ];
199
200 let shared = detect_shared(&records, &existing_edges);
201 assert_eq!(shared.len(), 1);
202 assert_eq!(shared[0].from, 0);
203 assert_eq!(shared[0].to, 1);
204 assert_eq!(shared[0].relation, Relation::Shares);
205 }
206
207 #[test]
208 fn test_no_shared_with_single_owner() {
209 let mut arc_inner_data = vec![0u8; 48];
210 arc_inner_data[0..8].copy_from_slice(&1usize.to_le_bytes());
211 arc_inner_data[8..16].copy_from_slice(&0usize.to_le_bytes());
212
213 let records = vec![
214 make_record(0, 0x1000, 24, None, TypeKind::Vec),
215 make_record(1, 0x3000, 48, Some(arc_inner_data), TypeKind::Buffer),
216 ];
217
218 let existing_edges = vec![RelationEdge {
219 from: 0,
220 to: 1,
221 relation: Relation::Owns,
222 }];
223
224 let shared = detect_shared(&records, &existing_edges);
225 assert!(shared.is_empty());
226 }
227
228 #[test]
229 fn test_no_shared_when_not_arc_like() {
230 let records = vec![
231 make_record(0, 0x1000, 24, None, TypeKind::Vec),
232 make_record(1, 0x2000, 24, None, TypeKind::Vec),
233 make_record(2, 0x3000, 4096, None, TypeKind::Buffer),
234 ];
235
236 let existing_edges = vec![
237 RelationEdge {
238 from: 0,
239 to: 2,
240 relation: Relation::Owns,
241 },
242 RelationEdge {
243 from: 1,
244 to: 2,
245 relation: Relation::Owns,
246 },
247 ];
248
249 let shared = detect_shared(&records, &existing_edges);
250 assert!(shared.is_empty());
251 }
252
253 #[test]
254 fn test_shared_three_owners() {
255 let mut arc_inner_data = vec![0u8; 48];
256 arc_inner_data[0..8].copy_from_slice(&3usize.to_le_bytes());
257 arc_inner_data[8..16].copy_from_slice(&0usize.to_le_bytes());
258
259 let records = vec![
260 make_record(0, 0x1000, 24, None, TypeKind::Vec),
261 make_record(1, 0x2000, 24, None, TypeKind::Vec),
262 make_record(2, 0x2500, 24, None, TypeKind::Vec),
263 make_record(3, 0x3000, 48, Some(arc_inner_data), TypeKind::Buffer),
264 ];
265
266 let existing_edges = vec![
267 RelationEdge {
268 from: 0,
269 to: 3,
270 relation: Relation::Owns,
271 },
272 RelationEdge {
273 from: 1,
274 to: 3,
275 relation: Relation::Owns,
276 },
277 RelationEdge {
278 from: 2,
279 to: 3,
280 relation: Relation::Owns,
281 },
282 ];
283
284 let shared = detect_shared(&records, &existing_edges);
285 assert_eq!(shared.len(), 3);
286 }
287
288 #[test]
289 fn test_looks_like_arc_rc_valid() {
290 let mut data = vec![0u8; 48];
291 data[0..8].copy_from_slice(&2usize.to_le_bytes());
292 data[8..16].copy_from_slice(&0usize.to_le_bytes());
293
294 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
295 assert!(looks_like_arc_rc(&record));
296 }
297
298 #[test]
299 fn test_looks_like_arc_rc_too_small() {
300 let record = make_record(0, 0x1000, 8, None, TypeKind::Buffer);
301 assert!(!looks_like_arc_rc(&record));
302 }
303
304 #[test]
305 fn test_looks_like_arc_rc_too_large() {
306 let record = make_record(0, 0x1000, 2048, None, TypeKind::Buffer);
307 assert!(!looks_like_arc_rc(&record));
308 }
309
310 #[test]
311 fn test_looks_like_arc_rc_no_memory() {
312 let record = make_record(0, 0x1000, 48, None, TypeKind::Buffer);
313 assert!(!looks_like_arc_rc(&record));
314 }
315
316 #[test]
317 fn test_looks_like_arc_rc_invalid_strong() {
318 let mut data = vec![0u8; 48];
319 data[0..8].copy_from_slice(&0usize.to_le_bytes());
320 data[8..16].copy_from_slice(&0usize.to_le_bytes());
321
322 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
323 assert!(!looks_like_arc_rc(&record));
324 }
325
326 #[test]
327 fn test_looks_like_arc_rc_invalid_weak() {
328 let mut data = vec![0u8; 48];
329 data[0..8].copy_from_slice(&1usize.to_le_bytes());
330 data[8..16].copy_from_slice(&9999usize.to_le_bytes());
331
332 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
333 assert!(!looks_like_arc_rc(&record));
334 }
335
336 #[test]
337 fn test_looks_like_arc_rc_strong_count_zero() {
338 let mut data = vec![0u8; 48];
340 data[0..8].copy_from_slice(&0usize.to_le_bytes());
341 data[8..16].copy_from_slice(&0usize.to_le_bytes());
342
343 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
344 assert!(!looks_like_arc_rc(&record));
345 }
346
347 #[test]
348 fn test_looks_like_arc_rc_strong_count_boundary_10000() {
349 let mut data = vec![0u8; 48];
351 data[0..8].copy_from_slice(&10000usize.to_le_bytes());
352 data[8..16].copy_from_slice(&0usize.to_le_bytes());
353
354 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
355 assert!(looks_like_arc_rc(&record));
356 }
357
358 #[test]
359 fn test_looks_like_arc_rc_strong_count_exceeds_10000() {
360 let mut data = vec![0u8; 48];
362 data[0..8].copy_from_slice(&10001usize.to_le_bytes());
363 data[8..16].copy_from_slice(&0usize.to_le_bytes());
364
365 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
366 assert!(!looks_like_arc_rc(&record));
367 }
368
369 #[test]
370 fn test_looks_like_arc_rc_weak_count_boundary_1000() {
371 let mut data = vec![0u8; 48];
373 data[0..8].copy_from_slice(&1usize.to_le_bytes());
374 data[8..16].copy_from_slice(&1000usize.to_le_bytes());
375
376 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
377 assert!(looks_like_arc_rc(&record));
378 }
379
380 #[test]
381 fn test_looks_like_arc_rc_weak_count_exceeds_1000() {
382 let mut data = vec![0u8; 48];
384 data[0..8].copy_from_slice(&1usize.to_le_bytes());
385 data[8..16].copy_from_slice(&1001usize.to_le_bytes());
386
387 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
388 assert!(!looks_like_arc_rc(&record));
389 }
390
391 #[test]
392 fn test_no_shared_for_regular_vec_type() {
393 let records = vec![
395 make_record(0, 0x1000, 24, None, TypeKind::Vec),
396 make_record(1, 0x2000, 24, None, TypeKind::Vec),
397 make_record(2, 0x3000, 64, Some(vec![0xAAu8; 64]), TypeKind::Buffer),
399 ];
400
401 let existing_edges = vec![
402 RelationEdge {
403 from: 0,
404 to: 2,
405 relation: Relation::Owns,
406 },
407 RelationEdge {
408 from: 1,
409 to: 2,
410 relation: Relation::Owns,
411 },
412 ];
413
414 let shared = detect_shared(&records, &existing_edges);
415 assert!(
416 shared.is_empty(),
417 "Regular Vec buffer should not produce Shared edges"
418 );
419 }
420
421 #[test]
422 fn test_shared_detection_with_rc_like_data() {
423 let mut rc_inner_data = vec![0u8; 32];
425 rc_inner_data[0..8].copy_from_slice(&2usize.to_le_bytes()); rc_inner_data[8..16].copy_from_slice(&1usize.to_le_bytes()); let records = vec![
429 make_record(0, 0x1000, 24, None, TypeKind::Vec),
430 make_record(1, 0x2000, 24, None, TypeKind::Vec),
431 make_record(2, 0x3000, 32, Some(rc_inner_data), TypeKind::Buffer),
432 ];
433
434 let existing_edges = vec![
435 RelationEdge {
436 from: 0,
437 to: 2,
438 relation: Relation::Owns,
439 },
440 RelationEdge {
441 from: 1,
442 to: 2,
443 relation: Relation::Owns,
444 },
445 ];
446
447 let shared = detect_shared(&records, &existing_edges);
448 assert_eq!(shared.len(), 1, "Rc-like data should produce Shared edge");
449 assert_eq!(shared[0].from, 0);
450 assert_eq!(shared[0].to, 1);
451 }
452
453 #[test]
454 fn test_stackowner_arc_clone_detection() {
455 let heap_ptr = 0x3000; let stack_ptr1 = 0x7ff0000; let stack_ptr2 = 0x7ff0008; let stack_ptr3 = 0x7ff0010; let records = vec![
463 make_record_with_stack_ptr(0, heap_ptr, stack_ptr1, 48, None, TypeKind::Buffer),
465 make_record_with_stack_ptr(1, heap_ptr, stack_ptr2, 48, None, TypeKind::Buffer),
467 make_record_with_stack_ptr(2, heap_ptr, stack_ptr3, 48, None, TypeKind::Buffer),
469 make_record(3, 0x4000, 24, None, TypeKind::Vec),
471 ];
472
473 let existing_edges = vec![];
474
475 let shared = detect_shared(&records, &existing_edges);
476
477 assert!(
480 !shared.is_empty(),
481 "StackOwner Arc clones should be detected"
482 );
483
484 let arc_clone_edges: Vec<_> = shared
486 .iter()
487 .filter(|e| matches!(e.relation, Relation::ArcClone))
488 .collect();
489 assert!(!arc_clone_edges.is_empty(), "Should have ArcClone edges");
490
491 println!(
492 "Detected {} ArcClone edges from 3 Arc clones",
493 arc_clone_edges.len()
494 );
495 }
496
497 #[test]
498 fn test_no_shared_with_only_one_owner() {
499 let mut arc_inner_data = vec![0u8; 48];
501 arc_inner_data[0..8].copy_from_slice(&1usize.to_le_bytes());
502 arc_inner_data[8..16].copy_from_slice(&0usize.to_le_bytes());
503
504 let records = vec![
505 make_record(0, 0x1000, 24, None, TypeKind::Vec),
506 make_record(1, 0x3000, 48, Some(arc_inner_data), TypeKind::Buffer),
507 ];
508
509 let existing_edges = vec![RelationEdge {
510 from: 0,
511 to: 1,
512 relation: Relation::Owns,
513 }];
514
515 let shared = detect_shared(&records, &existing_edges);
516 assert!(
517 shared.is_empty(),
518 "Single owner should not produce Shared edges"
519 );
520 }
521}