1use crate::analysis::relation_inference::{InferenceRecord, Relation, RelationEdge};
19
20const MIN_SHARED_OWNERS: usize = 2;
21
22pub fn detect_shared(
23 records: &[InferenceRecord],
24 existing_edges: &[RelationEdge],
25) -> Vec<RelationEdge> {
26 let mut owners_of: Vec<Vec<usize>> = vec![Vec::new(); records.len()];
27 for edge in existing_edges {
28 if edge.relation == Relation::Owner {
29 owners_of[edge.to].push(edge.from);
30 }
31 }
32
33 let mut relations = Vec::new();
34
35 for (target_id, owners) in owners_of.iter().enumerate() {
36 if owners.len() < MIN_SHARED_OWNERS {
37 continue;
38 }
39
40 let target = &records[target_id];
41
42 if !looks_like_arc_rc(target) {
43 continue;
44 }
45
46 for i in 0..owners.len() {
47 for j in (i + 1)..owners.len() {
48 relations.push(RelationEdge {
49 from: owners[i],
50 to: owners[j],
51 relation: Relation::Shared,
52 });
53 }
54 }
55 }
56
57 relations
58}
59
60fn looks_like_arc_rc(record: &InferenceRecord) -> bool {
61 if record.size < 16 || record.size > 1024 {
62 return false;
63 }
64
65 let memory = match &record.memory {
66 Some(m) => m,
67 None => return false,
68 };
69
70 if memory.len() < 16 {
71 return false;
72 }
73
74 let strong = memory.read_usize(0).unwrap_or(usize::MAX);
75 let weak = memory.read_usize(8).unwrap_or(usize::MAX);
76
77 let strong_valid = (1..=1000).contains(&strong);
78 let weak_valid = weak <= 100;
79
80 strong_valid && weak_valid
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::analysis::unsafe_inference::{OwnedMemoryView, TypeKind};
87
88 fn make_record(
89 id: usize,
90 ptr: usize,
91 size: usize,
92 memory: Option<Vec<u8>>,
93 type_kind: TypeKind,
94 ) -> InferenceRecord {
95 InferenceRecord {
96 id,
97 ptr,
98 size,
99 memory: memory.map(OwnedMemoryView::new),
100 type_kind,
101 confidence: 80,
102 call_stack_hash: None,
103 alloc_time: 0,
104 }
105 }
106
107 #[test]
108 fn test_shared_detection_basic() {
109 let mut arc_inner_data = vec![0u8; 48];
110 arc_inner_data[0..8].copy_from_slice(&2usize.to_le_bytes());
111 arc_inner_data[8..16].copy_from_slice(&0usize.to_le_bytes());
112
113 let records = vec![
114 make_record(0, 0x1000, 24, None, TypeKind::Vec),
115 make_record(1, 0x2000, 24, None, TypeKind::Vec),
116 make_record(2, 0x3000, 48, Some(arc_inner_data), TypeKind::Buffer),
117 ];
118
119 let existing_edges = vec![
120 RelationEdge {
121 from: 0,
122 to: 2,
123 relation: Relation::Owner,
124 },
125 RelationEdge {
126 from: 1,
127 to: 2,
128 relation: Relation::Owner,
129 },
130 ];
131
132 let shared = detect_shared(&records, &existing_edges);
133 assert_eq!(shared.len(), 1);
134 assert_eq!(shared[0].from, 0);
135 assert_eq!(shared[0].to, 1);
136 assert_eq!(shared[0].relation, Relation::Shared);
137 }
138
139 #[test]
140 fn test_no_shared_with_single_owner() {
141 let mut arc_inner_data = vec![0u8; 48];
142 arc_inner_data[0..8].copy_from_slice(&1usize.to_le_bytes());
143 arc_inner_data[8..16].copy_from_slice(&0usize.to_le_bytes());
144
145 let records = vec![
146 make_record(0, 0x1000, 24, None, TypeKind::Vec),
147 make_record(1, 0x3000, 48, Some(arc_inner_data), TypeKind::Buffer),
148 ];
149
150 let existing_edges = vec![RelationEdge {
151 from: 0,
152 to: 1,
153 relation: Relation::Owner,
154 }];
155
156 let shared = detect_shared(&records, &existing_edges);
157 assert!(shared.is_empty());
158 }
159
160 #[test]
161 fn test_no_shared_when_not_arc_like() {
162 let records = vec![
163 make_record(0, 0x1000, 24, None, TypeKind::Vec),
164 make_record(1, 0x2000, 24, None, TypeKind::Vec),
165 make_record(2, 0x3000, 4096, None, TypeKind::Buffer),
166 ];
167
168 let existing_edges = vec![
169 RelationEdge {
170 from: 0,
171 to: 2,
172 relation: Relation::Owner,
173 },
174 RelationEdge {
175 from: 1,
176 to: 2,
177 relation: Relation::Owner,
178 },
179 ];
180
181 let shared = detect_shared(&records, &existing_edges);
182 assert!(shared.is_empty());
183 }
184
185 #[test]
186 fn test_shared_three_owners() {
187 let mut arc_inner_data = vec![0u8; 48];
188 arc_inner_data[0..8].copy_from_slice(&3usize.to_le_bytes());
189 arc_inner_data[8..16].copy_from_slice(&0usize.to_le_bytes());
190
191 let records = vec![
192 make_record(0, 0x1000, 24, None, TypeKind::Vec),
193 make_record(1, 0x2000, 24, None, TypeKind::Vec),
194 make_record(2, 0x2500, 24, None, TypeKind::Vec),
195 make_record(3, 0x3000, 48, Some(arc_inner_data), TypeKind::Buffer),
196 ];
197
198 let existing_edges = vec![
199 RelationEdge {
200 from: 0,
201 to: 3,
202 relation: Relation::Owner,
203 },
204 RelationEdge {
205 from: 1,
206 to: 3,
207 relation: Relation::Owner,
208 },
209 RelationEdge {
210 from: 2,
211 to: 3,
212 relation: Relation::Owner,
213 },
214 ];
215
216 let shared = detect_shared(&records, &existing_edges);
217 assert_eq!(shared.len(), 3);
218 }
219
220 #[test]
221 fn test_looks_like_arc_rc_valid() {
222 let mut data = vec![0u8; 48];
223 data[0..8].copy_from_slice(&2usize.to_le_bytes());
224 data[8..16].copy_from_slice(&0usize.to_le_bytes());
225
226 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
227 assert!(looks_like_arc_rc(&record));
228 }
229
230 #[test]
231 fn test_looks_like_arc_rc_too_small() {
232 let record = make_record(0, 0x1000, 8, None, TypeKind::Buffer);
233 assert!(!looks_like_arc_rc(&record));
234 }
235
236 #[test]
237 fn test_looks_like_arc_rc_too_large() {
238 let record = make_record(0, 0x1000, 2048, None, TypeKind::Buffer);
239 assert!(!looks_like_arc_rc(&record));
240 }
241
242 #[test]
243 fn test_looks_like_arc_rc_no_memory() {
244 let record = make_record(0, 0x1000, 48, None, TypeKind::Buffer);
245 assert!(!looks_like_arc_rc(&record));
246 }
247
248 #[test]
249 fn test_looks_like_arc_rc_invalid_strong() {
250 let mut data = vec![0u8; 48];
251 data[0..8].copy_from_slice(&0usize.to_le_bytes());
252 data[8..16].copy_from_slice(&0usize.to_le_bytes());
253
254 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
255 assert!(!looks_like_arc_rc(&record));
256 }
257
258 #[test]
259 fn test_looks_like_arc_rc_invalid_weak() {
260 let mut data = vec![0u8; 48];
261 data[0..8].copy_from_slice(&1usize.to_le_bytes());
262 data[8..16].copy_from_slice(&9999usize.to_le_bytes());
263
264 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
265 assert!(!looks_like_arc_rc(&record));
266 }
267
268 #[test]
269 fn test_looks_like_arc_rc_strong_count_zero() {
270 let mut data = vec![0u8; 48];
272 data[0..8].copy_from_slice(&0usize.to_le_bytes());
273 data[8..16].copy_from_slice(&0usize.to_le_bytes());
274
275 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
276 assert!(!looks_like_arc_rc(&record));
277 }
278
279 #[test]
280 fn test_looks_like_arc_rc_strong_count_boundary_1000() {
281 let mut data = vec![0u8; 48];
283 data[0..8].copy_from_slice(&1000usize.to_le_bytes());
284 data[8..16].copy_from_slice(&0usize.to_le_bytes());
285
286 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
287 assert!(looks_like_arc_rc(&record));
288 }
289
290 #[test]
291 fn test_looks_like_arc_rc_strong_count_exceeds_1000() {
292 let mut data = vec![0u8; 48];
294 data[0..8].copy_from_slice(&1001usize.to_le_bytes());
295 data[8..16].copy_from_slice(&0usize.to_le_bytes());
296
297 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
298 assert!(!looks_like_arc_rc(&record));
299 }
300
301 #[test]
302 fn test_looks_like_arc_rc_weak_count_boundary_100() {
303 let mut data = vec![0u8; 48];
305 data[0..8].copy_from_slice(&1usize.to_le_bytes());
306 data[8..16].copy_from_slice(&100usize.to_le_bytes());
307
308 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
309 assert!(looks_like_arc_rc(&record));
310 }
311
312 #[test]
313 fn test_looks_like_arc_rc_weak_count_exceeds_100() {
314 let mut data = vec![0u8; 48];
316 data[0..8].copy_from_slice(&1usize.to_le_bytes());
317 data[8..16].copy_from_slice(&101usize.to_le_bytes());
318
319 let record = make_record(0, 0x1000, 48, Some(data), TypeKind::Buffer);
320 assert!(!looks_like_arc_rc(&record));
321 }
322
323 #[test]
324 fn test_no_shared_for_regular_vec_type() {
325 let records = vec![
327 make_record(0, 0x1000, 24, None, TypeKind::Vec),
328 make_record(1, 0x2000, 24, None, TypeKind::Vec),
329 make_record(2, 0x3000, 64, Some(vec![0xAAu8; 64]), TypeKind::Buffer),
331 ];
332
333 let existing_edges = vec![
334 RelationEdge {
335 from: 0,
336 to: 2,
337 relation: Relation::Owner,
338 },
339 RelationEdge {
340 from: 1,
341 to: 2,
342 relation: Relation::Owner,
343 },
344 ];
345
346 let shared = detect_shared(&records, &existing_edges);
347 assert!(
348 shared.is_empty(),
349 "Regular Vec buffer should not produce Shared edges"
350 );
351 }
352
353 #[test]
354 fn test_shared_detection_with_rc_like_data() {
355 let mut rc_inner_data = vec![0u8; 32];
357 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![
361 make_record(0, 0x1000, 24, None, TypeKind::Vec),
362 make_record(1, 0x2000, 24, None, TypeKind::Vec),
363 make_record(2, 0x3000, 32, Some(rc_inner_data), TypeKind::Buffer),
364 ];
365
366 let existing_edges = vec![
367 RelationEdge {
368 from: 0,
369 to: 2,
370 relation: Relation::Owner,
371 },
372 RelationEdge {
373 from: 1,
374 to: 2,
375 relation: Relation::Owner,
376 },
377 ];
378
379 let shared = detect_shared(&records, &existing_edges);
380 assert_eq!(shared.len(), 1, "Rc-like data should produce Shared edge");
381 assert_eq!(shared[0].from, 0);
382 assert_eq!(shared[0].to, 1);
383 }
384
385 #[test]
386 fn test_no_shared_with_only_one_owner() {
387 let mut arc_inner_data = vec![0u8; 48];
389 arc_inner_data[0..8].copy_from_slice(&1usize.to_le_bytes());
390 arc_inner_data[8..16].copy_from_slice(&0usize.to_le_bytes());
391
392 let records = vec![
393 make_record(0, 0x1000, 24, None, TypeKind::Vec),
394 make_record(1, 0x3000, 48, Some(arc_inner_data), TypeKind::Buffer),
395 ];
396
397 let existing_edges = vec![RelationEdge {
398 from: 0,
399 to: 1,
400 relation: Relation::Owner,
401 }];
402
403 let shared = detect_shared(&records, &existing_edges);
404 assert!(
405 shared.is_empty(),
406 "Single owner should not produce Shared edges"
407 );
408 }
409}