From eec02f141a7bed2d1fc418a672335b448efa8e19 Mon Sep 17 00:00:00 2001 From: Masato Imai Date: Tue, 19 Aug 2025 14:26:16 +0000 Subject: [PATCH] check io, msr bitmaps, nmi, vmcs shadow, ept, int --- .../src/vmm/x86_64/intel/auditor/controls.rs | 169 +++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/nel_os_kernel/src/vmm/x86_64/intel/auditor/controls.rs b/nel_os_kernel/src/vmm/x86_64/intel/auditor/controls.rs index 75fe504..ff66633 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/auditor/controls.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/auditor/controls.rs @@ -2,7 +2,14 @@ use x86::{msr, vmx::vmcs}; use crate::vmm::x86_64::{ common::read_msr, - intel::{self, vmread}, + intel::{ + self, + vmcs::controls::{ + PinBasedVmExecutionControls, PrimaryProcessorBasedVmExecutionControls, + SecondaryProcessorBasedVmExecutionControls, + }, + vmread, + }, }; pub fn check_vmcs_control_fields() -> Result<(), &'static str> { @@ -21,10 +28,160 @@ pub fn check_vmcs_control_fields() -> Result<(), &'static str> { check_cr3_target()?; check_io_bitmap()?; + check_msr_bitmap()?; + + check_nmi()?; + + check_vmcs_shadowing()?; + check_ept_violation_exception_info()?; + check_interrupt()?; + + check_ept()?; Ok(()) } +fn is_valid_ept_ptr(ept_ptr: u64) -> Result<(), &'static str> { + let memory_type = ept_ptr & 0b111; + if memory_type != 0 && memory_type != 6 { + return Err("VMCS EPT pointer memory type is not valid (must be 0 or 6)"); + } + + let walk_length = (ept_ptr >> 3) & 0b111; + if walk_length != 3 { + return Err("VMCS EPT pointer walk length is not valid (must be 3)"); + } + + if ept_ptr & 0xf00 != 0 { + return Err("VMCS EPT pointer reserved bits are not zero"); + } + + if !is_valid_phys_addr(ept_ptr) { + return Err("VMCS EPT pointer is not a valid physical address"); + } + + Ok(()) +} + +fn check_ept() -> Result<(), &'static str> { + let secondary_exec_ctrl = SecondaryProcessorBasedVmExecutionControls::read()?; + + if secondary_exec_ctrl.ept() { + let ept_ptr = vmread(vmcs::control::EPTP_FULL)?; + is_valid_ept_ptr(ept_ptr)?; + } else { + if secondary_exec_ctrl.unrestricted_guest() { + return Err( + "VMCS Secondary processor-based execution controls field: EPT is not set while unrestricted guest is set", + ); + } + if secondary_exec_ctrl.mode_based_control_ept() { + return Err( + "VMCS Secondary processor-based execution controls field: EPT is not set while mode-based control for EPT is set", + ); + } + } + + Ok(()) +} + +fn check_interrupt() -> Result<(), &'static str> { + let primary_exec_ctrl = PrimaryProcessorBasedVmExecutionControls::read()?; + let secondary_exec_ctrl = SecondaryProcessorBasedVmExecutionControls::read()?; + let pin_ctrl = PinBasedVmExecutionControls::read()?; + + if primary_exec_ctrl.use_tpr_shadow() { + let virtual_apic_page_addr = vmread(vmcs::control::VIRT_APIC_ADDR_FULL)?; + if !is_valid_page_aligned_phys_addr(virtual_apic_page_addr) { + return Err( + "VMCS virtual APIC page address is not a valid page-aligned physical address", + ); + } + + if !secondary_exec_ctrl.virtual_interrupt_delivery() { + if !pin_ctrl.external_interrupt_exiting() { + return Err( + "VMCS Pin-based execution controls field: External interrupt exiting is not set while virtual interrupt delivery is set", + ); + } + } else { + // TODO + } + } else { + if secondary_exec_ctrl.virtualize_x2apic_mode() + || secondary_exec_ctrl.apic_register_virtualization() + || secondary_exec_ctrl.virtual_interrupt_delivery() + { + return Err( + "VMCS Primary processor-based execution controls field: Use TPR shadow is not set while virtualize x2APIC mode, APIC register virtualization, or virtual interrupt delivery is set", + ); + } + } + + // TODO + + Ok(()) +} + +fn check_ept_violation_exception_info() -> Result<(), &'static str> { + let secondary_exec_ctrl = SecondaryProcessorBasedVmExecutionControls::read()?; + + if secondary_exec_ctrl.ept_violation() { + let exception_info = vmread(vmcs::control::VIRT_EXCEPTION_INFO_ADDR_FULL)?; + + if is_valid_page_aligned_phys_addr(exception_info) { + return Err("VMCS EPT violation exception info address is not a valid page-aligned physical address"); + } + } + + Ok(()) +} + +fn check_vmcs_shadowing() -> Result<(), &'static str> { + let secondary_exec_ctrl = SecondaryProcessorBasedVmExecutionControls::read()?; + + if !secondary_exec_ctrl.vmcs_shadowing() { + return Ok(()); + } + + let vmcs_vmread_bitmap = vmread(vmcs::control::VMREAD_BITMAP_ADDR_FULL)?; + let vmcs_vmwrite_bitmap = vmread(vmcs::control::VMWRITE_BITMAP_ADDR_FULL)?; + + if !is_valid_page_aligned_phys_addr(vmcs_vmread_bitmap) { + return Err("VMCS VMREAD bitmap address is not a valid page-aligned physical address"); + } + + if !is_valid_page_aligned_phys_addr(vmcs_vmwrite_bitmap) { + return Err("VMCS VMWRITE bitmap address is not a valid page-aligned physical address"); + } + + Ok(()) +} + +fn check_nmi() -> Result<(), &'static str> { + let pin_ctrl = PinBasedVmExecutionControls::read()?; + + if !pin_ctrl.nmi_exiting() && pin_ctrl.virtual_nmi() { + return Err( + "VMCS Pin-based execution controls field: NMI exiting and virtual NMI are both set", + ); + } + + let exec_ctrl = PrimaryProcessorBasedVmExecutionControls::read()?; + + if !pin_ctrl.virtual_nmi() && exec_ctrl.nmi_window() { + return Err( + "VMCS Pin-based execution controls field: Interrupt-window exiting and virtual NMI are both not set", + ); + } + + Ok(()) +} + +fn is_valid_phys_addr(addr: u64) -> bool { + (addr & !((1 << 40) - 1)) == 0 +} + fn is_valid_page_aligned_phys_addr(addr: u64) -> bool { (addr & (!((1 << 40) - 1) | 0xfff)) == 0 } @@ -39,6 +196,16 @@ fn check_cr3_target() -> Result<(), &'static str> { Ok(()) } +fn check_msr_bitmap() -> Result<(), &'static str> { + let vmcs_msr_bitmap = vmread(vmcs::control::MSR_BITMAPS_ADDR_FULL)?; + + if !is_valid_page_aligned_phys_addr(vmcs_msr_bitmap) { + return Err("VMCS MSR bitmap address is not a valid page-aligned physical address"); + } + + Ok(()) +} + fn check_io_bitmap() -> Result<(), &'static str> { let vmcs_io_bitmap_a = vmread(vmcs::control::IO_BITMAP_A_ADDR_FULL)?; let vmcs_io_bitmap_b = vmread(vmcs::control::IO_BITMAP_B_ADDR_FULL)?;