Skip to main content

memf_linux/
shared_mem_anomaly.rs

1//! Shared memory forensics / anomaly detection.
2
3use memf_core::object_reader::ObjectReader;
4use memf_format::PhysicalMemoryProvider;
5
6use crate::types::SharedMemAnomalyInfo;
7use crate::Result;
8
9/// Classify whether a shared memory segment has an anomalously high attach
10/// count (`nattch`) that exceeds the given threshold.
11///
12/// Returns `true` if `nattch > threshold`, indicating that an unusually large
13/// number of processes have the segment attached — a potential indicator of
14/// cross-process shellcode staging.
15pub fn is_suspicious_shm(nattch: u64, threshold: u64) -> bool {
16    nattch > threshold
17}
18
19/// Scan for shared memory anomalies (executable memfd, ELF headers, cross-uid sharing).
20///
21/// Returns `Ok(vec![])` as a stub until full implementation is added.
22pub fn scan_shared_mem_anomalies<P: PhysicalMemoryProvider>(
23    reader: &ObjectReader<P>,
24) -> Result<Vec<SharedMemAnomalyInfo>> {
25    let _ = reader;
26    Ok(vec![])
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32    use memf_core::test_builders::PageTableBuilder;
33    use memf_core::vas::{TranslationMode, VirtualAddressSpace};
34    use memf_symbols::isf::IsfResolver;
35    use memf_symbols::test_builders::IsfBuilder;
36
37    fn make_minimal_reader() -> ObjectReader<memf_core::test_builders::SyntheticPhysMem> {
38        let isf = IsfBuilder::new().build_json();
39        let resolver = IsfResolver::from_value(&isf).unwrap();
40        let (cr3, mem) = PageTableBuilder::new().build();
41        let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
42        ObjectReader::new(vas, Box::new(resolver))
43    }
44
45    #[test]
46    fn empty_memory_returns_ok_empty() {
47        let reader = make_minimal_reader();
48        let result = scan_shared_mem_anomalies(&reader);
49        assert!(result.is_ok(), "should succeed with minimal reader");
50        assert!(
51            result.unwrap().is_empty(),
52            "empty memory → no shared mem anomalies"
53        );
54    }
55
56    #[test]
57    fn result_is_vec_of_shared_mem_anomaly_info() {
58        let reader = make_minimal_reader();
59        let result: Result<Vec<SharedMemAnomalyInfo>> = scan_shared_mem_anomalies(&reader);
60        assert!(result.is_ok());
61    }
62
63    #[test]
64    fn shared_mem_anomaly_info_fields_constructible() {
65        let info = SharedMemAnomalyInfo {
66            pid: 500,
67            comm: "loader".to_string(),
68            shm_base: 0x7f00_0000_0000,
69            shm_size: 65536,
70            is_memfd: true,
71            is_executable: true,
72            is_cross_uid: false,
73            has_elf_header: true,
74        };
75        assert_eq!(info.pid, 500);
76        assert!(info.is_memfd);
77        assert!(info.is_executable);
78        assert!(!info.is_cross_uid);
79        assert!(info.has_elf_header);
80    }
81
82    #[test]
83    fn shared_mem_anomaly_info_serializes() {
84        let info = SharedMemAnomalyInfo {
85            pid: 88,
86            comm: "inject".to_string(),
87            shm_base: 0x1000,
88            shm_size: 4096,
89            is_memfd: false,
90            is_executable: true,
91            is_cross_uid: true,
92            has_elf_header: false,
93        };
94        let json = serde_json::to_string(&info).unwrap();
95        assert!(json.contains("\"pid\":88"));
96        assert!(json.contains("\"is_executable\":true"));
97        assert!(json.contains("\"is_cross_uid\":true"));
98    }
99
100    // --- classifier helper tests (genuine RED: function does not exist yet) ---
101
102    #[test]
103    fn nattch_above_threshold_is_suspicious_shm() {
104        assert!(is_suspicious_shm(100, 50));
105    }
106
107    #[test]
108    fn nattch_at_threshold_is_not_suspicious() {
109        assert!(!is_suspicious_shm(50, 50));
110    }
111
112    #[test]
113    fn nattch_below_threshold_is_not_suspicious() {
114        assert!(!is_suspicious_shm(10, 50));
115    }
116}