1use memf_core::object_reader::ObjectReader;
9use memf_format::PhysicalMemoryProvider;
10
11use crate::Result;
12
13#[derive(Debug, Clone, serde::Serialize)]
15pub struct IpcShmInfo {
16 pub key: u32,
18 pub shmid: u32,
20 pub size: u64,
22 pub owner_pid: u32,
24 pub creator_pid: u32,
26 pub permissions: u32,
28 pub num_attaches: u32,
30}
31
32#[derive(Debug, Clone, serde::Serialize)]
34pub struct IpcSemInfo {
35 pub key: u32,
37 pub semid: u32,
39 pub num_sems: u32,
41 pub owner_pid: u32,
43 pub permissions: u32,
45}
46
47const MAX_IPC_IDS: usize = 32_768;
49
50const MAX_XA_DEPTH: usize = 8;
52
53const XA_NODE_SLOTS: usize = 64;
55
56fn walk_xarray<P: memf_format::PhysicalMemoryProvider>(
67 reader: &ObjectReader<P>,
68 xa_head: u64,
69 depth: usize,
70 entries: &mut Vec<u64>,
71) {
72 if xa_head == 0 || depth > MAX_XA_DEPTH || entries.len() >= MAX_IPC_IDS {
73 return;
74 }
75
76 if xa_head & 2 == 0 {
77 let ptr = xa_head & !3u64;
79 if ptr != 0 {
80 entries.push(ptr);
81 }
82 return;
83 }
84
85 let node_addr = xa_head & !3u64;
87
88 let slots_offset = reader
91 .symbols()
92 .field_offset("xa_node", "slots")
93 .unwrap_or(0);
94
95 let slots_vaddr = node_addr + slots_offset;
97 let raw = match reader.read_bytes(slots_vaddr, XA_NODE_SLOTS * 8) {
98 Ok(b) => b,
99 Err(_) => return,
100 };
101
102 for i in 0..XA_NODE_SLOTS {
103 if entries.len() >= MAX_IPC_IDS {
104 break;
105 }
106 let slot = raw[i * 8..(i + 1) * 8]
107 .try_into()
108 .map_or(0, u64::from_le_bytes);
109 if slot == 0 {
110 continue;
111 }
112 walk_xarray(reader, slot, depth + 1, entries);
113 }
114}
115
116pub fn walk_shm_segments<P: PhysicalMemoryProvider>(
121 reader: &ObjectReader<P>,
122) -> Result<Vec<IpcShmInfo>> {
123 let shm_ids_addr = match reader.symbols().symbol_address("shm_ids") {
124 Some(addr) => addr,
125 None => return Ok(Vec::new()),
126 };
127
128 let in_use: u32 = match reader.read_field(shm_ids_addr, "ipc_ids", "in_use") {
129 Ok(v) => v,
130 Err(_) => return Ok(Vec::new()),
131 };
132
133 if in_use == 0 {
134 return Ok(Vec::new());
135 }
136
137 let ipcs_idr_offset = reader
139 .symbols()
140 .field_offset("ipc_ids", "ipcs_idr")
141 .unwrap_or(0);
142 let idr_rt_offset = reader.symbols().field_offset("idr", "idr_rt").unwrap_or(0);
143 let xa_head_offset = reader
144 .symbols()
145 .field_offset("radix_tree_root", "xa_head")
146 .unwrap_or(0);
147
148 let xa_head_addr = shm_ids_addr + ipcs_idr_offset + idr_rt_offset + xa_head_offset;
149 let first_entry: u64 = match reader.read_field(xa_head_addr, "radix_tree_root", "xa_head") {
150 Ok(v) => v,
151 Err(_) => return Ok(Vec::new()),
152 };
153
154 let mut segments = Vec::new();
155
156 if first_entry == 0 {
157 return Ok(segments);
158 }
159
160 let mut addrs = Vec::new();
162 walk_xarray(reader, first_entry, 0, &mut addrs);
163
164 let shm_perm_offset = reader
165 .symbols()
166 .field_offset("shmid_kernel", "shm_perm")
167 .unwrap_or(0);
168
169 for addr in addrs {
170 let perm_base = addr + shm_perm_offset;
171
172 let key: u32 = match reader.read_field(perm_base, "kern_ipc_perm", "key") {
173 Ok(v) => v,
174 Err(_) => continue,
175 };
176 let id: u32 = match reader.read_field(perm_base, "kern_ipc_perm", "id") {
177 Ok(v) => v,
178 Err(_) => continue,
179 };
180 let mode: u32 = match reader.read_field(perm_base, "kern_ipc_perm", "mode") {
181 Ok(v) => v,
182 Err(_) => continue,
183 };
184
185 let size: u64 = match reader.read_field(addr, "shmid_kernel", "shm_segsz") {
186 Ok(v) => v,
187 Err(_) => continue,
188 };
189 let cprid: u32 = match reader.read_field(addr, "shmid_kernel", "shm_cprid") {
190 Ok(v) => v,
191 Err(_) => continue,
192 };
193 let lprid: u32 = match reader.read_field(addr, "shmid_kernel", "shm_lprid") {
194 Ok(v) => v,
195 Err(_) => continue,
196 };
197 let nattch: u32 = match reader.read_field(addr, "shmid_kernel", "shm_nattch") {
198 Ok(v) => v,
199 Err(_) => continue,
200 };
201
202 segments.push(IpcShmInfo {
203 key,
204 shmid: id,
205 size,
206 owner_pid: lprid,
207 creator_pid: cprid,
208 permissions: mode,
209 num_attaches: nattch,
210 });
211 }
212
213 Ok(segments)
214}
215
216pub fn walk_semaphores<P: PhysicalMemoryProvider>(
221 reader: &ObjectReader<P>,
222) -> Result<Vec<IpcSemInfo>> {
223 let sem_ids_addr = match reader.symbols().symbol_address("sem_ids") {
224 Some(addr) => addr,
225 None => return Ok(Vec::new()),
226 };
227
228 let in_use: u32 = match reader.read_field(sem_ids_addr, "ipc_ids", "in_use") {
229 Ok(v) => v,
230 Err(_) => return Ok(Vec::new()),
231 };
232
233 if in_use == 0 {
234 return Ok(Vec::new());
235 }
236
237 let ipcs_idr_offset = reader
239 .symbols()
240 .field_offset("ipc_ids", "ipcs_idr")
241 .unwrap_or(0);
242 let idr_rt_offset = reader.symbols().field_offset("idr", "idr_rt").unwrap_or(0);
243 let xa_head_offset = reader
244 .symbols()
245 .field_offset("radix_tree_root", "xa_head")
246 .unwrap_or(0);
247
248 let xa_head_addr = sem_ids_addr + ipcs_idr_offset + idr_rt_offset + xa_head_offset;
249 let first_entry: u64 = match reader.read_field(xa_head_addr, "radix_tree_root", "xa_head") {
250 Ok(v) => v,
251 Err(_) => return Ok(Vec::new()),
252 };
253
254 let mut semaphores = Vec::new();
255
256 if first_entry == 0 {
257 return Ok(semaphores);
258 }
259
260 let mut addrs = Vec::new();
262 walk_xarray(reader, first_entry, 0, &mut addrs);
263
264 let sem_perm_offset = reader
265 .symbols()
266 .field_offset("sem_array", "sem_perm")
267 .unwrap_or(0);
268
269 for addr in addrs {
270 let perm_base = addr + sem_perm_offset;
271
272 let key: u32 = match reader.read_field(perm_base, "kern_ipc_perm", "key") {
273 Ok(v) => v,
274 Err(_) => continue,
275 };
276 let id: u32 = match reader.read_field(perm_base, "kern_ipc_perm", "id") {
277 Ok(v) => v,
278 Err(_) => continue,
279 };
280 let mode: u32 = match reader.read_field(perm_base, "kern_ipc_perm", "mode") {
281 Ok(v) => v,
282 Err(_) => continue,
283 };
284
285 let nsems: u32 = match reader.read_field(addr, "sem_array", "sem_nsems") {
286 Ok(v) => v,
287 Err(_) => continue,
288 };
289 let owner_pid: u32 = reader
292 .read_field(addr, "sem_array", "sem_otime_high")
293 .unwrap_or(0);
294
295 semaphores.push(IpcSemInfo {
296 key,
297 semid: id,
298 num_sems: nsems,
299 owner_pid,
300 permissions: mode,
301 });
302 }
303
304 Ok(semaphores)
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use memf_core::test_builders::{flags, PageTableBuilder, SyntheticPhysMem};
311 use memf_core::vas::{TranslationMode, VirtualAddressSpace};
312 use memf_symbols::isf::IsfResolver;
313 use memf_symbols::test_builders::IsfBuilder;
314
315 fn make_empty_reader() -> ObjectReader<SyntheticPhysMem> {
318 let isf = IsfBuilder::new().build_json();
319 let resolver = IsfResolver::from_value(&isf).unwrap();
320 let vaddr: u64 = 0xFFFF_8000_0010_0000;
321 let paddr: u64 = 0x0080_0000;
322 let data = vec![0u8; 4096];
323 let (cr3, mem) = PageTableBuilder::new()
324 .map_4k(vaddr, paddr, flags::WRITABLE)
325 .write_phys(paddr, &data)
326 .build();
327 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
328 ObjectReader::new(vas, Box::new(resolver))
329 }
330
331 fn make_shm_reader(data: &[u8], vaddr: u64, paddr: u64) -> ObjectReader<SyntheticPhysMem> {
334 let isf = IsfBuilder::new()
335 .add_struct("ipc_ids", 64)
337 .add_field("ipc_ids", "in_use", 0, "unsigned int")
338 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
339 .add_struct("idr", 32)
342 .add_field("idr", "idr_rt", 0, "radix_tree_root")
343 .add_struct("radix_tree_root", 16)
345 .add_field("radix_tree_root", "xa_head", 0, "pointer")
346 .add_struct("kern_ipc_perm", 64)
348 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
349 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
350 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
351 .add_struct("shmid_kernel", 128)
353 .add_field("shmid_kernel", "shm_perm", 0, "kern_ipc_perm")
354 .add_field("shmid_kernel", "shm_segsz", 64, "unsigned long")
355 .add_field("shmid_kernel", "shm_cprid", 72, "unsigned int")
356 .add_field("shmid_kernel", "shm_lprid", 76, "unsigned int")
357 .add_field("shmid_kernel", "shm_nattch", 80, "unsigned int")
358 .add_symbol("shm_ids", vaddr)
359 .build_json();
360
361 let resolver = IsfResolver::from_value(&isf).unwrap();
362 let (cr3, mem) = PageTableBuilder::new()
363 .map_4k(vaddr, paddr, flags::WRITABLE)
364 .write_phys(paddr, data)
365 .build();
366 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
367 ObjectReader::new(vas, Box::new(resolver))
368 }
369
370 #[test]
371 fn walk_shm_no_symbol() {
372 let reader = make_empty_reader();
373 let result = walk_shm_segments(&reader).unwrap();
374 assert!(
375 result.is_empty(),
376 "no shm_ids symbol should yield empty vec"
377 );
378 }
379
380 #[test]
381 fn walk_sem_no_symbol() {
382 let reader = make_empty_reader();
383 let result = walk_semaphores(&reader).unwrap();
384 assert!(
385 result.is_empty(),
386 "no sem_ids symbol should yield empty vec"
387 );
388 }
389
390 #[test]
391 fn walk_shm_in_use_zero_returns_empty() {
392 let vaddr: u64 = 0xFFFF_8000_0010_0000;
394 let paddr: u64 = 0x0080_0000;
395 let data = vec![0u8; 4096];
396 let _ = data[0]; let isf = IsfBuilder::new()
400 .add_struct("ipc_ids", 64)
401 .add_field("ipc_ids", "in_use", 0, "unsigned int")
402 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
403 .add_struct("idr", 32)
404 .add_field("idr", "idr_rt", 0, "radix_tree_root")
405 .add_struct("radix_tree_root", 16)
406 .add_field("radix_tree_root", "xa_head", 0, "pointer")
407 .add_struct("kern_ipc_perm", 64)
408 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
409 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
410 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
411 .add_struct("shmid_kernel", 128)
412 .add_field("shmid_kernel", "shm_perm", 0, "kern_ipc_perm")
413 .add_field("shmid_kernel", "shm_segsz", 64, "unsigned long")
414 .add_field("shmid_kernel", "shm_cprid", 72, "unsigned int")
415 .add_field("shmid_kernel", "shm_lprid", 76, "unsigned int")
416 .add_field("shmid_kernel", "shm_nattch", 80, "unsigned int")
417 .add_symbol("shm_ids", vaddr)
418 .build_json();
419
420 let resolver = IsfResolver::from_value(&isf).unwrap();
421 let (cr3, mem) = PageTableBuilder::new()
422 .map_4k(vaddr, paddr, flags::WRITABLE)
423 .write_phys(paddr, &data)
424 .build();
425 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
426 let reader = ObjectReader::new(vas, Box::new(resolver));
427
428 let result = walk_shm_segments(&reader).unwrap();
429 assert!(result.is_empty(), "in_use == 0 should yield empty vec");
430 }
431
432 #[test]
433 fn walk_sem_in_use_zero_returns_empty() {
434 let vaddr: u64 = 0xFFFF_8000_0020_0000;
436 let paddr: u64 = 0x0090_0000;
437 let data = vec![0u8; 4096]; let isf = IsfBuilder::new()
440 .add_struct("ipc_ids", 64)
441 .add_field("ipc_ids", "in_use", 0, "unsigned int")
442 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
443 .add_struct("idr", 32)
444 .add_field("idr", "idr_rt", 0, "radix_tree_root")
445 .add_struct("radix_tree_root", 16)
446 .add_field("radix_tree_root", "xa_head", 0, "pointer")
447 .add_struct("kern_ipc_perm", 64)
448 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
449 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
450 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
451 .add_struct("sem_array", 128)
452 .add_field("sem_array", "sem_perm", 0, "kern_ipc_perm")
453 .add_field("sem_array", "sem_nsems", 64, "unsigned int")
454 .add_field("sem_array", "sem_otime_high", 68, "unsigned int")
455 .add_symbol("sem_ids", vaddr)
456 .build_json();
457
458 let resolver = IsfResolver::from_value(&isf).unwrap();
459 let (cr3, mem) = PageTableBuilder::new()
460 .map_4k(vaddr, paddr, flags::WRITABLE)
461 .write_phys(paddr, &data)
462 .build();
463 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
464 let reader = ObjectReader::new(vas, Box::new(resolver));
465
466 let result = walk_semaphores(&reader).unwrap();
467 assert!(
468 result.is_empty(),
469 "in_use == 0 should yield empty vec for semaphores"
470 );
471 }
472
473 #[test]
478 fn walk_sem_in_use_nonzero_xa_head_zero_returns_empty() {
479 let vaddr: u64 = 0xFFFF_8800_00A0_0000;
482 let paddr: u64 = 0x00B0_0000;
483 let mut data = vec![0u8; 4096];
484
485 data[0..4].copy_from_slice(&1u32.to_le_bytes());
487 let isf = IsfBuilder::new()
492 .add_struct("ipc_ids", 64)
493 .add_field("ipc_ids", "in_use", 0, "unsigned int")
494 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
495 .add_struct("idr", 32)
496 .add_field("idr", "idr_rt", 0, "radix_tree_root")
497 .add_struct("radix_tree_root", 16)
498 .add_field("radix_tree_root", "xa_head", 0, "pointer")
499 .add_struct("kern_ipc_perm", 64)
500 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
501 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
502 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
503 .add_struct("sem_array", 128)
504 .add_field("sem_array", "sem_perm", 0, "kern_ipc_perm")
505 .add_field("sem_array", "sem_nsems", 64, "unsigned int")
506 .add_field("sem_array", "sem_otime_high", 68, "unsigned int")
507 .add_symbol("sem_ids", vaddr)
508 .build_json();
509
510 let resolver = IsfResolver::from_value(&isf).unwrap();
511 let (cr3, mem) = PageTableBuilder::new()
512 .map_4k(vaddr, paddr, flags::WRITABLE)
513 .write_phys(paddr, &data)
514 .build();
515 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
516 let reader = ObjectReader::new(vas, Box::new(resolver));
517
518 let result = walk_semaphores(&reader).unwrap_or_default();
519 assert!(
520 result.is_empty(),
521 "xa_head==0 with in_use>0 should yield empty semaphore list"
522 );
523 }
524
525 #[test]
530 fn walk_shm_in_use_nonzero_xa_head_zero_returns_empty() {
531 let vaddr: u64 = 0xFFFF_8800_00C0_0000;
533 let paddr: u64 = 0x00D0_0000;
534 let mut data = vec![0u8; 4096];
535
536 data[0..4].copy_from_slice(&1u32.to_le_bytes());
538 let isf = IsfBuilder::new()
541 .add_struct("ipc_ids", 64)
542 .add_field("ipc_ids", "in_use", 0, "unsigned int")
543 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
544 .add_struct("idr", 32)
545 .add_field("idr", "idr_rt", 0, "radix_tree_root")
546 .add_struct("radix_tree_root", 16)
547 .add_field("radix_tree_root", "xa_head", 0, "pointer")
548 .add_struct("kern_ipc_perm", 64)
549 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
550 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
551 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
552 .add_struct("shmid_kernel", 128)
553 .add_field("shmid_kernel", "shm_perm", 0, "kern_ipc_perm")
554 .add_field("shmid_kernel", "shm_segsz", 64, "unsigned long")
555 .add_field("shmid_kernel", "shm_cprid", 72, "unsigned int")
556 .add_field("shmid_kernel", "shm_lprid", 76, "unsigned int")
557 .add_field("shmid_kernel", "shm_nattch", 80, "unsigned int")
558 .add_symbol("shm_ids", vaddr)
559 .build_json();
560
561 let resolver = IsfResolver::from_value(&isf).unwrap();
562 let (cr3, mem) = PageTableBuilder::new()
563 .map_4k(vaddr, paddr, flags::WRITABLE)
564 .write_phys(paddr, &data)
565 .build();
566 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
567 let reader = ObjectReader::new(vas, Box::new(resolver));
568
569 let result = walk_shm_segments(&reader).unwrap_or_default();
570 assert!(
571 result.is_empty(),
572 "xa_head==0 with in_use>0 should yield empty shm list"
573 );
574 }
575
576 #[test]
582 fn walk_semaphores_single_semaphore_set() {
583 let vaddr: u64 = 0xFFFF_8800_00F0_0000;
584 let paddr: u64 = 0x00F0_0000;
585 let mut data = vec![0u8; 4096];
586
587 data[0..4].copy_from_slice(&1u32.to_le_bytes()); let sem_array_addr = vaddr + 0x200;
597 data[8..16].copy_from_slice(&sem_array_addr.to_le_bytes()); let base = 0x200usize;
606 data[base..base + 4].copy_from_slice(&0xBEEFu32.to_le_bytes());
607 data[base + 4..base + 8].copy_from_slice(&77u32.to_le_bytes());
608 data[base + 8..base + 12].copy_from_slice(&0o600u32.to_le_bytes());
609 data[base + 64..base + 68].copy_from_slice(&5u32.to_le_bytes());
610 data[base + 68..base + 72].copy_from_slice(&999u32.to_le_bytes());
611
612 let isf = IsfBuilder::new()
613 .add_struct("ipc_ids", 64)
614 .add_field("ipc_ids", "in_use", 0, "unsigned int")
615 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
616 .add_struct("idr", 32)
617 .add_field("idr", "idr_rt", 0, "radix_tree_root")
618 .add_struct("radix_tree_root", 16)
619 .add_field("radix_tree_root", "xa_head", 0, "pointer")
620 .add_struct("kern_ipc_perm", 64)
621 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
622 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
623 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
624 .add_struct("sem_array", 128)
625 .add_field("sem_array", "sem_perm", 0, "kern_ipc_perm")
626 .add_field("sem_array", "sem_nsems", 64, "unsigned int")
627 .add_field("sem_array", "sem_otime_high", 68, "unsigned int")
628 .add_symbol("sem_ids", vaddr)
629 .build_json();
630
631 let resolver = IsfResolver::from_value(&isf).unwrap();
632 let (cr3, mem) = PageTableBuilder::new()
633 .map_4k(vaddr, paddr, flags::WRITABLE)
634 .write_phys(paddr, &data)
635 .build();
636 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
637 let reader = ObjectReader::new(vas, Box::new(resolver));
638
639 let result = walk_semaphores(&reader).unwrap();
640 assert_eq!(result.len(), 1, "should find one semaphore set");
641 assert_eq!(result[0].key, 0xBEEF);
642 assert_eq!(result[0].semid, 77);
643 assert_eq!(result[0].num_sems, 5);
644 assert_eq!(result[0].owner_pid, 999);
645 assert_eq!(result[0].permissions, 0o600);
646 }
647
648 #[test]
654 fn walk_shm_in_use_read_fails_returns_empty() {
655 let vaddr: u64 = 0xFFFF_8800_00F1_0000;
658 let paddr: u64 = 0x00F1_0000;
659 let data = vec![1u8; 4096]; let isf = IsfBuilder::new()
662 .add_struct("ipc_ids", 64)
663 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
665 .add_symbol("shm_ids", vaddr)
666 .build_json();
667
668 let resolver = IsfResolver::from_value(&isf).unwrap();
669 let (cr3, mem) = PageTableBuilder::new()
670 .map_4k(vaddr, paddr, flags::WRITABLE)
671 .write_phys(paddr, &data)
672 .build();
673 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
674 let reader = ObjectReader::new(vas, Box::new(resolver));
675
676 let result = walk_shm_segments(&reader).unwrap();
677 assert!(result.is_empty(), "missing in_use field → Err → empty vec");
678 }
679
680 #[test]
686 fn walk_sem_in_use_read_fails_returns_empty() {
687 let vaddr: u64 = 0xFFFF_8800_00F2_0000;
688 let paddr: u64 = 0x00F2_0000;
689 let data = vec![1u8; 4096];
690
691 let isf = IsfBuilder::new()
692 .add_struct("ipc_ids", 64)
693 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
695 .add_symbol("sem_ids", vaddr)
696 .build_json();
697
698 let resolver = IsfResolver::from_value(&isf).unwrap();
699 let (cr3, mem) = PageTableBuilder::new()
700 .map_4k(vaddr, paddr, flags::WRITABLE)
701 .write_phys(paddr, &data)
702 .build();
703 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
704 let reader = ObjectReader::new(vas, Box::new(resolver));
705
706 let result = walk_semaphores(&reader).unwrap();
707 assert!(result.is_empty(), "missing in_use field → Err → empty vec");
708 }
709
710 #[test]
715 fn ipc_shm_info_clone_debug_serialize() {
716 let info = IpcShmInfo {
717 key: 0xDEAD,
718 shmid: 42,
719 size: 65536,
720 owner_pid: 100,
721 creator_pid: 200,
722 permissions: 0o644,
723 num_attaches: 3,
724 };
725 let cloned = info.clone();
726 assert_eq!(cloned.key, 0xDEAD);
727 let dbg = format!("{cloned:?}");
728 assert!(dbg.contains("shmid"));
729 let json = serde_json::to_string(&cloned).unwrap();
730 assert!(json.contains("\"key\":57005"));
731 }
732
733 #[test]
734 fn ipc_sem_info_clone_debug_serialize() {
735 let info = IpcSemInfo {
736 key: 0xCAFE,
737 semid: 7,
738 num_sems: 4,
739 owner_pid: 99,
740 permissions: 0o755,
741 };
742 let cloned = info.clone();
743 assert_eq!(cloned.semid, 7);
744 let dbg = format!("{cloned:?}");
745 assert!(dbg.contains("num_sems"));
746 let json = serde_json::to_string(&cloned).unwrap();
747 assert!(json.contains("\"semid\":7"));
748 }
749
750 #[test]
757 fn walk_shm_in_use_gt1_returns_one_entry() {
758 let vaddr: u64 = 0xFFFF_8800_00E0_0000;
762 let paddr: u64 = 0x00E0_0000;
763 let mut data = vec![0u8; 4096];
764
765 data[0..4].copy_from_slice(&5u32.to_le_bytes()); let shm_kernel_addr = vaddr + 0x200;
768 data[8..16].copy_from_slice(&shm_kernel_addr.to_le_bytes()); let base = 0x200usize;
772 data[base..base + 4].copy_from_slice(&0x1234u32.to_le_bytes()); data[base + 4..base + 8].copy_from_slice(&99u32.to_le_bytes()); data[base + 8..base + 12].copy_from_slice(&0o644u32.to_le_bytes()); data[base + 64..base + 72].copy_from_slice(&8192u64.to_le_bytes()); data[base + 72..base + 76].copy_from_slice(&500u32.to_le_bytes()); data[base + 76..base + 80].copy_from_slice(&501u32.to_le_bytes()); data[base + 80..base + 84].copy_from_slice(&1u32.to_le_bytes()); let isf = IsfBuilder::new()
781 .add_struct("ipc_ids", 64)
782 .add_field("ipc_ids", "in_use", 0, "unsigned int")
783 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
784 .add_struct("idr", 32)
785 .add_field("idr", "idr_rt", 0, "radix_tree_root")
786 .add_struct("radix_tree_root", 16)
787 .add_field("radix_tree_root", "xa_head", 0, "pointer")
788 .add_struct("kern_ipc_perm", 64)
789 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
790 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
791 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
792 .add_struct("shmid_kernel", 128)
793 .add_field("shmid_kernel", "shm_perm", 0, "kern_ipc_perm")
794 .add_field("shmid_kernel", "shm_segsz", 64, "unsigned long")
795 .add_field("shmid_kernel", "shm_cprid", 72, "unsigned int")
796 .add_field("shmid_kernel", "shm_lprid", 76, "unsigned int")
797 .add_field("shmid_kernel", "shm_nattch", 80, "unsigned int")
798 .add_symbol("shm_ids", vaddr)
799 .build_json();
800
801 let resolver = IsfResolver::from_value(&isf).unwrap();
802 let (cr3, mem) = PageTableBuilder::new()
803 .map_4k(vaddr, paddr, flags::WRITABLE)
804 .write_phys(paddr, &data)
805 .build();
806 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
807 let reader = ObjectReader::new(vas, Box::new(resolver));
808
809 let result = walk_shm_segments(&reader).unwrap();
810 assert_eq!(
812 result.len(),
813 1,
814 "in_use=5 but only first xa_head entry is recoverable"
815 );
816 assert_eq!(result[0].key, 0x1234);
817 }
818
819 #[test]
820 fn walk_sem_in_use_gt1_returns_one_entry() {
821 let vaddr: u64 = 0xFFFF_8800_00D0_0000;
823 let paddr: u64 = 0x00D8_0000;
824 let mut data = vec![0u8; 4096];
825
826 data[0..4].copy_from_slice(&3u32.to_le_bytes()); let sem_array_addr = vaddr + 0x200;
829 data[8..16].copy_from_slice(&sem_array_addr.to_le_bytes()); let base = 0x200usize;
832 data[base..base + 4].copy_from_slice(&0xABCDu32.to_le_bytes()); data[base + 4..base + 8].copy_from_slice(&55u32.to_le_bytes()); data[base + 8..base + 12].copy_from_slice(&0o700u32.to_le_bytes()); data[base + 64..base + 68].copy_from_slice(&2u32.to_le_bytes()); data[base + 68..base + 72].copy_from_slice(&0u32.to_le_bytes()); let isf = IsfBuilder::new()
839 .add_struct("ipc_ids", 64)
840 .add_field("ipc_ids", "in_use", 0, "unsigned int")
841 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
842 .add_struct("idr", 32)
843 .add_field("idr", "idr_rt", 0, "radix_tree_root")
844 .add_struct("radix_tree_root", 16)
845 .add_field("radix_tree_root", "xa_head", 0, "pointer")
846 .add_struct("kern_ipc_perm", 64)
847 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
848 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
849 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
850 .add_struct("sem_array", 128)
851 .add_field("sem_array", "sem_perm", 0, "kern_ipc_perm")
852 .add_field("sem_array", "sem_nsems", 64, "unsigned int")
853 .add_field("sem_array", "sem_otime_high", 68, "unsigned int")
854 .add_symbol("sem_ids", vaddr)
855 .build_json();
856
857 let resolver = IsfResolver::from_value(&isf).unwrap();
858 let (cr3, mem) = PageTableBuilder::new()
859 .map_4k(vaddr, paddr, flags::WRITABLE)
860 .write_phys(paddr, &data)
861 .build();
862 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
863 let reader = ObjectReader::new(vas, Box::new(resolver));
864
865 let result = walk_semaphores(&reader).unwrap();
866 assert_eq!(
868 result.len(),
869 1,
870 "in_use=3 but only first xa_head entry is recoverable"
871 );
872 assert_eq!(result[0].key, 0xABCD);
873 assert_eq!(result[0].semid, 55);
874 }
875
876 fn make_isf_with_xa_node() -> serde_json::Value {
882 IsfBuilder::new()
883 .add_struct("ipc_ids", 64)
885 .add_field("ipc_ids", "in_use", 0, "unsigned int")
886 .add_field("ipc_ids", "ipcs_idr", 8, "idr")
887 .add_struct("idr", 32)
889 .add_field("idr", "idr_rt", 0, "radix_tree_root")
890 .add_struct("radix_tree_root", 16)
892 .add_field("radix_tree_root", "xa_head", 0, "pointer")
893 .add_struct("xa_node", 512)
895 .add_field("xa_node", "slots", 0, "pointer")
896 .add_struct("kern_ipc_perm", 64)
898 .add_field("kern_ipc_perm", "key", 0, "unsigned int")
899 .add_field("kern_ipc_perm", "id", 4, "unsigned int")
900 .add_field("kern_ipc_perm", "mode", 8, "unsigned int")
901 .add_struct("sem_array", 128)
903 .add_field("sem_array", "sem_perm", 0, "kern_ipc_perm")
904 .add_field("sem_array", "sem_nsems", 64, "unsigned int")
905 .add_field("sem_array", "sem_otime_high", 68, "unsigned int")
906 .add_struct("shmid_kernel", 128)
908 .add_field("shmid_kernel", "shm_perm", 0, "kern_ipc_perm")
909 .add_field("shmid_kernel", "shm_segsz", 64, "unsigned long")
910 .add_field("shmid_kernel", "shm_cprid", 72, "unsigned int")
911 .add_field("shmid_kernel", "shm_lprid", 76, "unsigned int")
912 .add_field("shmid_kernel", "shm_nattch", 80, "unsigned int")
913 .build_json()
914 }
915
916 #[test]
917 fn walk_semaphores_returns_multiple_entries() {
918 let vaddr_a: u64 = 0xFFFF_8800_0100_0000; let paddr_a: u64 = 0x00A0_0000;
932 let vaddr_b: u64 = 0xFFFF_8800_0101_0000; let paddr_b: u64 = 0x00A1_0000;
934 let vaddr_c: u64 = 0xFFFF_8800_0102_0000; let paddr_c: u64 = 0x00A2_0000;
936 let vaddr_d: u64 = 0xFFFF_8800_0103_0000; let paddr_d: u64 = 0x00A3_0000;
938 let vaddr_e: u64 = 0xFFFF_8800_0104_0000; let paddr_e: u64 = 0x00A4_0000;
940
941 let mut page_a = vec![0u8; 4096];
942 let mut page_b = vec![0u8; 4096];
943 let mut page_c = vec![0u8; 4096];
944 let mut page_d = vec![0u8; 4096];
945 let mut page_e = vec![0u8; 4096];
946
947 page_a[0..4].copy_from_slice(&3u32.to_le_bytes());
950 let xa_head_val = vaddr_b | 2;
954 page_a[8..16].copy_from_slice(&xa_head_val.to_le_bytes());
955
956 page_b[0..8].copy_from_slice(&vaddr_c.to_le_bytes());
961 page_b[8..16].copy_from_slice(&vaddr_d.to_le_bytes());
962 page_b[16..24].copy_from_slice(&vaddr_e.to_le_bytes());
963 page_c[0..4].copy_from_slice(&0x1001u32.to_le_bytes()); page_c[4..8].copy_from_slice(&10u32.to_le_bytes()); page_c[8..12].copy_from_slice(&0o600u32.to_le_bytes()); page_c[64..68].copy_from_slice(&3u32.to_le_bytes()); page_c[68..72].copy_from_slice(&100u32.to_le_bytes()); page_d[0..4].copy_from_slice(&0x1002u32.to_le_bytes());
974 page_d[4..8].copy_from_slice(&11u32.to_le_bytes());
975 page_d[8..12].copy_from_slice(&0o644u32.to_le_bytes());
976 page_d[64..68].copy_from_slice(&2u32.to_le_bytes());
977 page_d[68..72].copy_from_slice(&101u32.to_le_bytes());
978
979 page_e[0..4].copy_from_slice(&0x1003u32.to_le_bytes());
981 page_e[4..8].copy_from_slice(&12u32.to_le_bytes());
982 page_e[8..12].copy_from_slice(&0o755u32.to_le_bytes());
983 page_e[64..68].copy_from_slice(&1u32.to_le_bytes());
984 page_e[68..72].copy_from_slice(&102u32.to_le_bytes());
985
986 let mut isf = make_isf_with_xa_node();
987 isf["symbols"]["sem_ids"] = serde_json::json!({ "address": vaddr_a });
989
990 let resolver = IsfResolver::from_value(&isf).unwrap();
991 let (cr3, mem) = PageTableBuilder::new()
992 .map_4k(vaddr_a, paddr_a, flags::WRITABLE)
993 .write_phys(paddr_a, &page_a)
994 .map_4k(vaddr_b, paddr_b, flags::WRITABLE)
995 .write_phys(paddr_b, &page_b)
996 .map_4k(vaddr_c, paddr_c, flags::WRITABLE)
997 .write_phys(paddr_c, &page_c)
998 .map_4k(vaddr_d, paddr_d, flags::WRITABLE)
999 .write_phys(paddr_d, &page_d)
1000 .map_4k(vaddr_e, paddr_e, flags::WRITABLE)
1001 .write_phys(paddr_e, &page_e)
1002 .build();
1003 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
1004 let reader = ObjectReader::new(vas, Box::new(resolver));
1005
1006 let result = walk_semaphores(&reader).unwrap();
1007 assert_eq!(
1008 result.len(),
1009 3,
1010 "XArray node with 3 slots must yield 3 semaphore sets"
1011 );
1012
1013 let mut keys: Vec<u32> = result.iter().map(|s| s.key).collect();
1015 keys.sort_unstable();
1016 assert_eq!(keys, vec![0x1001, 0x1002, 0x1003]);
1017 }
1018
1019 #[test]
1020 fn walk_msgqueues_returns_multiple_entries() {
1021 let vaddr_a: u64 = 0xFFFF_8800_0200_0000;
1027 let paddr_a: u64 = 0x00B0_0000;
1028 let vaddr_b: u64 = 0xFFFF_8800_0201_0000;
1029 let paddr_b: u64 = 0x00B1_0000;
1030 let vaddr_c: u64 = 0xFFFF_8800_0202_0000;
1031 let paddr_c: u64 = 0x00B2_0000;
1032 let vaddr_d: u64 = 0xFFFF_8800_0203_0000;
1033 let paddr_d: u64 = 0x00B3_0000;
1034 let vaddr_e: u64 = 0xFFFF_8800_0204_0000;
1035 let paddr_e: u64 = 0x00B4_0000;
1036
1037 let mut page_a = vec![0u8; 4096];
1038 let mut page_b = vec![0u8; 4096];
1039 let mut page_c = vec![0u8; 4096];
1040 let mut page_d = vec![0u8; 4096];
1041 let mut page_e = vec![0u8; 4096];
1042
1043 page_a[0..4].copy_from_slice(&3u32.to_le_bytes()); let xa_head_val = vaddr_b | 2;
1045 page_a[8..16].copy_from_slice(&xa_head_val.to_le_bytes());
1046
1047 page_b[0..8].copy_from_slice(&vaddr_c.to_le_bytes());
1048 page_b[8..16].copy_from_slice(&vaddr_d.to_le_bytes());
1049 page_b[16..24].copy_from_slice(&vaddr_e.to_le_bytes());
1050
1051 page_c[0..4].copy_from_slice(&0x2001u32.to_le_bytes());
1053 page_c[4..8].copy_from_slice(&20u32.to_le_bytes());
1054 page_c[8..12].copy_from_slice(&0o600u32.to_le_bytes());
1055 page_c[64..68].copy_from_slice(&5u32.to_le_bytes());
1056
1057 page_d[0..4].copy_from_slice(&0x2002u32.to_le_bytes());
1058 page_d[4..8].copy_from_slice(&21u32.to_le_bytes());
1059 page_d[8..12].copy_from_slice(&0o644u32.to_le_bytes());
1060 page_d[64..68].copy_from_slice(&4u32.to_le_bytes());
1061
1062 page_e[0..4].copy_from_slice(&0x2003u32.to_le_bytes());
1063 page_e[4..8].copy_from_slice(&22u32.to_le_bytes());
1064 page_e[8..12].copy_from_slice(&0o755u32.to_le_bytes());
1065 page_e[64..68].copy_from_slice(&3u32.to_le_bytes());
1066
1067 let mut isf = make_isf_with_xa_node();
1068 isf["symbols"]["sem_ids"] = serde_json::json!({ "address": vaddr_a });
1069
1070 let resolver = IsfResolver::from_value(&isf).unwrap();
1071 let (cr3, mem) = PageTableBuilder::new()
1072 .map_4k(vaddr_a, paddr_a, flags::WRITABLE)
1073 .write_phys(paddr_a, &page_a)
1074 .map_4k(vaddr_b, paddr_b, flags::WRITABLE)
1075 .write_phys(paddr_b, &page_b)
1076 .map_4k(vaddr_c, paddr_c, flags::WRITABLE)
1077 .write_phys(paddr_c, &page_c)
1078 .map_4k(vaddr_d, paddr_d, flags::WRITABLE)
1079 .write_phys(paddr_d, &page_d)
1080 .map_4k(vaddr_e, paddr_e, flags::WRITABLE)
1081 .write_phys(paddr_e, &page_e)
1082 .build();
1083 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
1084 let reader = ObjectReader::new(vas, Box::new(resolver));
1085
1086 let result = walk_semaphores(&reader).unwrap();
1087 assert_eq!(
1088 result.len(),
1089 3,
1090 "second XArray node with 3 slots must yield 3 entries"
1091 );
1092
1093 let mut keys: Vec<u32> = result.iter().map(|s| s.key).collect();
1094 keys.sort_unstable();
1095 assert_eq!(keys, vec![0x2001, 0x2002, 0x2003]);
1096 }
1097
1098 #[test]
1099 fn walk_shm_single_segment() {
1100 let vaddr: u64 = 0xFFFF_8000_0010_0000;
1101 let paddr: u64 = 0x0080_0000;
1102 let mut data = vec![0u8; 4096];
1103
1104 data[0..4].copy_from_slice(&1u32.to_le_bytes()); let shm_kernel_addr = vaddr + 0x200;
1113 data[8..16].copy_from_slice(&shm_kernel_addr.to_le_bytes());
1115
1116 let base = 0x200;
1125 data[base..base + 4].copy_from_slice(&0xDEADu32.to_le_bytes());
1126 data[base + 4..base + 8].copy_from_slice(&42u32.to_le_bytes());
1127 data[base + 8..base + 12].copy_from_slice(&0x1B6u32.to_le_bytes());
1128 data[base + 64..base + 72].copy_from_slice(&65536u64.to_le_bytes());
1129 data[base + 72..base + 76].copy_from_slice(&1000u32.to_le_bytes());
1130 data[base + 76..base + 80].copy_from_slice(&2000u32.to_le_bytes());
1131 data[base + 80..base + 84].copy_from_slice(&3u32.to_le_bytes());
1132
1133 let reader = make_shm_reader(&data, vaddr, paddr);
1134 let segments = walk_shm_segments(&reader).unwrap();
1135
1136 assert_eq!(segments.len(), 1);
1137 assert_eq!(segments[0].key, 0xDEAD);
1138 assert_eq!(segments[0].shmid, 42);
1139 assert_eq!(segments[0].size, 65536);
1140 assert_eq!(segments[0].creator_pid, 1000);
1141 assert_eq!(segments[0].owner_pid, 2000);
1142 assert_eq!(segments[0].permissions, 0x1B6);
1143 assert_eq!(segments[0].num_attaches, 3);
1144 }
1145}