Skip to main content

memf_linux/
vdso_tamper.rs

1//! vDSO tampering detection.
2
3use memf_core::object_reader::ObjectReader;
4use memf_format::PhysicalMemoryProvider;
5
6use crate::types::VdsoTamperInfo;
7use crate::Result;
8
9/// Classify whether a vDSO region has been tampered with.
10///
11/// Returns `true` if any bytes differ from the canonical kernel copy
12/// (`diff_byte_count > 0`). `_vdso_size` is accepted for future use
13/// (e.g. percentage-based thresholds) but is currently unused.
14pub fn is_vdso_tampered(diff_byte_count: usize, _vdso_size: usize) -> bool {
15    diff_byte_count > 0
16}
17
18/// Scan for vDSO regions that differ from the canonical kernel copy.
19///
20/// Returns `Ok(vec![])` as a stub until full implementation is added.
21pub fn scan_vdso_tampering<P: PhysicalMemoryProvider>(
22    reader: &ObjectReader<P>,
23) -> Result<Vec<VdsoTamperInfo>> {
24    let _ = reader;
25    Ok(vec![])
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    use memf_core::test_builders::PageTableBuilder;
32    use memf_core::vas::{TranslationMode, VirtualAddressSpace};
33    use memf_symbols::isf::IsfResolver;
34    use memf_symbols::test_builders::IsfBuilder;
35
36    fn make_minimal_reader() -> ObjectReader<memf_core::test_builders::SyntheticPhysMem> {
37        let isf = IsfBuilder::new().build_json();
38        let resolver = IsfResolver::from_value(&isf).unwrap();
39        let (cr3, mem) = PageTableBuilder::new().build();
40        let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
41        ObjectReader::new(vas, Box::new(resolver))
42    }
43
44    #[test]
45    fn empty_memory_returns_ok_empty() {
46        let reader = make_minimal_reader();
47        let result = scan_vdso_tampering(&reader);
48        assert!(result.is_ok(), "should succeed with minimal reader");
49        assert!(
50            result.unwrap().is_empty(),
51            "empty memory → no vDSO tampering"
52        );
53    }
54
55    #[test]
56    fn result_is_vec_of_vdso_tamper_info() {
57        let reader = make_minimal_reader();
58        let result: Result<Vec<VdsoTamperInfo>> = scan_vdso_tampering(&reader);
59        assert!(result.is_ok());
60    }
61
62    #[test]
63    fn vdso_tamper_info_fields_constructible() {
64        let info = VdsoTamperInfo {
65            pid: 100,
66            comm: "bash".to_string(),
67            vdso_base: 0x7fff_0000_0000,
68            vdso_size: 0x2000,
69            differs_from_canonical: true,
70            diff_byte_count: 8,
71        };
72        assert_eq!(info.pid, 100);
73        assert_eq!(info.vdso_size, 0x2000);
74        assert!(info.differs_from_canonical);
75        assert_eq!(info.diff_byte_count, 8);
76    }
77
78    #[test]
79    fn vdso_tamper_info_serializes() {
80        let info = VdsoTamperInfo {
81            pid: 55,
82            comm: "malware".to_string(),
83            vdso_base: 0xDEAD_0000,
84            vdso_size: 4096,
85            differs_from_canonical: true,
86            diff_byte_count: 16,
87        };
88        let json = serde_json::to_string(&info).unwrap();
89        assert!(json.contains("\"pid\":55"));
90        assert!(json.contains("\"differs_from_canonical\":true"));
91        assert!(json.contains("\"diff_byte_count\":16"));
92    }
93
94    // --- classifier helper tests (genuine RED: function does not exist yet) ---
95
96    #[test]
97    fn nonzero_diff_bytes_is_tampered() {
98        assert!(is_vdso_tampered(16, 4096));
99    }
100
101    #[test]
102    fn single_byte_diff_is_tampered() {
103        assert!(is_vdso_tampered(1, 4096));
104    }
105
106    #[test]
107    fn zero_diff_is_clean() {
108        assert!(!is_vdso_tampered(0, 4096));
109    }
110}