diff --git a/Cargo.toml b/Cargo.toml index 71d9a73d..c2f22c70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "lib"] [features] # Xen driver -xen = ["xenctrl", "xenstore", "xenforeignmemory", "libc"] +xen = ["xenctrl", "xenstore", "xenforeignmemory", "libc", "xenevtchn", "xenvmevent-sys"] # KVM driver kvm = ["kvmi"] # VirtualBox driver @@ -28,10 +28,12 @@ hyper-v = ["winapi", "widestring", "ntapi", "vid-sys"] log = "0.4.8" env_logger = "0.7.1" libc = { version = "0.2.58", optional = true } -xenctrl = { git = "https://github.com/Wenzel/xenctrl", optional = true } +xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "pagefault", optional = true } xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } kvmi = { version = "0.2.1", optional = true } +xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "event-notification", optional = true} +xenvmevent-sys = { git = "https://github.com/Wenzel/xenvmevent-sys", optional = true} fdp = { git = "https://github.com/Wenzel/fdp", optional = true } winapi = { version = "0.3.8", features = ["tlhelp32", "winnt", "handleapi", "securitybaseapi"], optional = true } widestring = { version = "0.4.0", optional = true } @@ -39,6 +41,7 @@ ntapi = { version = "0.3.3", optional = true } vid-sys = { version = "0.3.0", features = ["deprecated-apis"], optional = true } bitflags = "1.2.1" cty = "0.2.1" +nix = "0.18.0" [dev-dependencies] ctrlc = "3.1.3" diff --git a/examples/cr-events.rs b/examples/cr-events.rs index 8bd89c2e..4810748f 100644 --- a/examples/cr-events.rs +++ b/examples/cr-events.rs @@ -42,10 +42,11 @@ fn toggle_cr_intercepts(drv: &mut Box, vec_cr: &Vec, let intercept = InterceptType::Cr(*cr); let status_str = if enabled { "Enabling" } else { "Disabling" }; println!("{} intercept on {:?}", status_str, cr); - for vcpu in 0..drv.get_vcpu_count().unwrap() { - drv.toggle_intercept(vcpu, intercept, enabled) - .expect(&format!("Failed to enable {:?}", cr)); - } + //for vcpu in 0..drv.get_vcpu_count().unwrap() { + let vcpu = 0; + drv.toggle_intercept(vcpu, intercept, enabled) + .expect(&format!("Failed to enable {:?}", cr)); + //} } drv.resume().expect("Failed to resume VM"); @@ -101,7 +102,7 @@ fn main() { // listen let mut i: u64 = 0; while running.load(Ordering::SeqCst) { - let event = drv.listen(1000).expect("Failed to listen for events"); + let event = drv.listen(10).expect("Failed to listen for events"); match event { Some(ev) => { let (cr_type, new, old) = match ev.kind { @@ -120,8 +121,8 @@ fn main() { "[{}] {} - {}: old value: 0x{:x} new value: 0x{:x}", ev_nb_output, vcpu_output, cr_output, old, new ); - drv.reply_event(ev, EventReplyType::Continue) - .expect("Failed to send event reply"); + // drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); i = i + 1; } None => println!("No events yet..."), diff --git a/examples/mem-events.rs b/examples/mem-events.rs index 30cd926a..647f89d9 100644 --- a/examples/mem-events.rs +++ b/examples/mem-events.rs @@ -6,11 +6,9 @@ use std::sync::Arc; use std::time::Instant; use microvmi::api::{ - Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable, + Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable, PAGE_SIZE, }; -const PAGE_SIZE: usize = 4096; - fn parse_args() -> ArgMatches<'static> { App::new(file!()) .version("0.1") @@ -25,7 +23,7 @@ fn toggle_pf_intercept(drv: &mut Box, enabled: bool) { let intercept = InterceptType::Pagefault; let status_str = if enabled { "Enabling" } else { "Disabling" }; println!("{} memory events", status_str); - for vcpu in 0..drv.get_vcpu_count().unwrap() { + for vcpu in 0..1 { drv.toggle_intercept(vcpu, intercept, enabled) .expect(&format!("Failed to enable page faults")); } @@ -39,7 +37,6 @@ fn main() { let matches = parse_args(); let domain_name = matches.value_of("vm_name").unwrap(); - // set CTRL-C handler let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -61,11 +58,11 @@ fn main() { //Code snippet to get page fault let max_addr = drv.get_max_physical_addr().unwrap(); - - for cur_addr in (0..max_addr).step_by(PAGE_SIZE) { + //println!("max_gpfn: {}", max_addr>>PAGE_SHIFT); + for cur_addr in (0..max_addr).step_by(PAGE_SIZE as usize) { let mut access: Access = drv.get_page_access(cur_addr).unwrap(); access &= !Access::X; - drv.set_page_access(cur_addr, access) + drv.set_page_access(cur_addr, !Access::X) .expect("failed to set page access"); } @@ -87,10 +84,10 @@ fn main() { let mut page_access = drv.get_page_access(gpa).expect("Failed to get page access"); //setting the access bits in the page due to which page fault occurred page_access |= pf_access; - drv.set_page_access(gpa, page_access) + drv.set_page_access(gpa, Access::RWX) .expect("Failed to set page access"); - drv.reply_event(ev, EventReplyType::Continue) - .expect("Failed to send event reply"); + //drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); i = i + 1; } None => println!("No events yet..."), diff --git a/examples/msr-events.rs b/examples/msr-events.rs index d496cf6e..43beb667 100644 --- a/examples/msr-events.rs +++ b/examples/msr-events.rs @@ -109,8 +109,8 @@ fn main() { let event = drv.listen(1000).expect("Failed to listen for events"); match event { Some(ev) => { - let (msr_type, new, old) = match ev.kind { - EventType::Msr { msr_type, new, old } => (msr_type, new, old), + let (msr_type, value) = match ev.kind { + EventType::Msr { msr_type, value } => (msr_type, value), _ => panic!("not msr event"), }; let msr_color = "blue"; @@ -118,8 +118,8 @@ fn main() { let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); let msr_output = format!("0x{:x}", msr_type).color(msr_color); println!( - "[{}] {} - {}: old value: 0x{:x} new value: 0x{:x}", - ev_nb_output, vcpu_output, msr_output, old, new + "[{}] {} - {}: new value: 0x{:x}", + ev_nb_output, vcpu_output, msr_output, value, ); drv.reply_event(ev, EventReplyType::Continue) .expect("Failed to send event reply"); diff --git a/examples/regs-dump.rs b/examples/regs-dump.rs index 9495f9d3..68295913 100644 --- a/examples/regs-dump.rs +++ b/examples/regs-dump.rs @@ -33,7 +33,7 @@ fn main() { println!("pausing the VM"); drv.pause().expect("Failed to pause VM"); - let total_vcpu_count: u16 = drv.get_vcpu_count().expect("Failed to get vcpu count"); + let total_vcpu_count: u16 = 1; for vcpu in 0..total_vcpu_count { println!("dumping registers on VCPU {}", vcpu); let regs = drv.read_registers(vcpu).expect("Failed to read registers"); diff --git a/src/api.rs b/src/api.rs index bc598423..bac73863 100644 --- a/src/api.rs +++ b/src/api.rs @@ -334,9 +334,7 @@ pub enum EventType { ///Type of model specific register msr_type: u32, /// new value after msr register has been intercepted by the guest. - new: u64, - /// old value before cr register has been intercepted by the guest. - old: u64, + value: u64, }, ///int3 interception Breakpoint { @@ -345,6 +343,7 @@ pub enum EventType { /// instruction length. Generally it should be one. Anything other than one implies malicious guest. insn_len: u8, }, + ///Pagefault interception Pagefault { /// Virtual memory address of the guest gva: u64, diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index 17262c67..d99c4c12 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -309,10 +309,9 @@ impl Introspectable for Kvm { new, old, }, - KVMiEventType::Msr { msr_type, new, old } => EventType::Msr { + KVMiEventType::Msr { msr_type, new, old: _ } => EventType::Msr { msr_type, - new, - old, + value: new, }, KVMiEventType::Breakpoint {gpa, insn_len } => EventType::Breakpoint { gpa, diff --git a/src/driver/xen.rs b/src/driver/xen.rs index f2bee070..4efb8593 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,19 +1,64 @@ use crate::api::{ - DriverInitParam, Introspectable, Registers, SegmentReg, SystemTableReg, X86Registers, + Access, CrType, DriverInitParam, Event, EventType, InterceptType, Introspectable, Registers, + SegmentReg, SystemTableReg, X86Registers, }; use libc::{PROT_READ, PROT_WRITE}; +use nix::poll::PollFlags; +use nix::poll::{poll, PollFd}; +use std::convert::TryInto; +use std::convert::{From, TryFrom}; use std::error::Error; +use std::mem; use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; -use xenctrl::XenControl; +use xenctrl::RING_HAS_UNCONSUMED_REQUESTS; +use xenctrl::{XenControl, XenCr, XenEventType, XenPageAccess}; +use xenevtchn::XenEventChannel; +use xenforeignmemory::XenForeignMem; use xenstore::{XBTransaction, Xs, XsOpenFlags}; +use xenvmevent_sys::{ + vm_event_back_ring, vm_event_response_t, VM_EVENT_FLAG_VCPU_PAUSED, VM_EVENT_INTERFACE_VERSION, +}; + +impl TryFrom for XenPageAccess { + type Error = &'static str; + fn try_from(access: Access) -> Result { + match access { + Access::NIL => Ok(XenPageAccess::NIL), + Access::R => Ok(XenPageAccess::R), + Access::W => Ok(XenPageAccess::W), + Access::RW => Ok(XenPageAccess::RW), + Access::X => Ok(XenPageAccess::X), + Access::RX => Ok(XenPageAccess::RX), + Access::WX => Ok(XenPageAccess::WX), + Access::RWX => Ok(XenPageAccess::RWX), + _ => Err("invalid access value"), + } + } +} + +impl From for Access { + fn from(access: XenPageAccess) -> Self { + match access { + XenPageAccess::NIL => Access::NIL, + XenPageAccess::R => Access::R, + XenPageAccess::W => Access::W, + XenPageAccess::RW => Access::RW, + XenPageAccess::X => Access::X, + XenPageAccess::RX => Access::RX, + XenPageAccess::WX => Access::WX, + XenPageAccess::RWX => Access::RWX, + } + } +} -// unit struct #[derive(Debug)] pub struct Xen { xc: XenControl, - xen_fgn: xenforeignmemory::XenForeignMem, + xev: XenEventChannel, + xen_fgn: XenForeignMem, dom_name: String, domid: u32, + back_ring: vm_event_back_ring, } impl Xen { @@ -35,21 +80,25 @@ impl Xen { if !found { panic!("Cannot find domain {}", domain_name); } - let xc = XenControl::new(None, None, 0).unwrap(); - let xen_fgn = xenforeignmemory::XenForeignMem::new().unwrap(); + + let mut xc = XenControl::new(None, None, 0).unwrap(); + let (_ring_page, back_ring, remote_port) = xc + .monitor_enable(cand_domid) + .expect("Failed to map event ring page"); + let xev = XenEventChannel::new(cand_domid, remote_port).unwrap(); + + let xen_fgn = XenForeignMem::new().unwrap(); let xen = Xen { xc, + xev, xen_fgn, dom_name: domain_name.to_string(), domid: cand_domid, + back_ring, }; debug!("Initialized {:#?}", xen); xen } - - fn close(&mut self) { - debug!("close"); - } } impl Introspectable for Xen { @@ -205,6 +254,113 @@ impl Introspectable for Xen { })) } + fn listen(&mut self, timeout: u32) -> Result, Box> { + let fd = self.xev.xenevtchn_fd()?; + let fd_struct = PollFd::new(fd, PollFlags::POLLIN | PollFlags::POLLERR); + let mut fds = [fd_struct]; + let mut vcpu: u16 = 0; + let mut event_type = unsafe { mem::MaybeUninit::::zeroed().assume_init() }; + let poll_result = poll(&mut fds, timeout.try_into().unwrap()).unwrap(); + let mut pending_event_port = -1; + if poll_result == 1 { + pending_event_port = self.xev.xenevtchn_pending()?; + if pending_event_port != -1 { + self.xev + .xenevtchn_unmask(pending_event_port.try_into().unwrap())?; + } + } + let back_ring_ptr = &mut self.back_ring; + let mut flag = false; + if poll_result > 0 + && self.xev.get_bind_port() == pending_event_port + && RING_HAS_UNCONSUMED_REQUESTS!(back_ring_ptr) != 0 + { + flag = true; + let req = self.xc.get_request(back_ring_ptr)?; + if req.version != VM_EVENT_INTERFACE_VERSION { + panic!("version mismatch"); + } + let xen_event_type = (self.xc.get_event_type(req)).unwrap(); + event_type = match xen_event_type { + XenEventType::Cr { cr_type, new, old } => EventType::Cr { + cr_type: match cr_type { + XenCr::Cr0 => CrType::Cr0, + XenCr::Cr3 => CrType::Cr3, + XenCr::Cr4 => CrType::Cr4, + }, + new, + old, + }, + XenEventType::Msr { msr_type, value } => EventType::Msr { msr_type, value }, + XenEventType::Breakpoint { gpa, insn_len } => { + EventType::Breakpoint { gpa, insn_len } + } + XenEventType::Pagefault { gva, gpa, access } => EventType::Pagefault { + gva, + gpa, + access: access.into(), + }, + }; + vcpu = req.vcpu_id.try_into().unwrap(); + let mut rsp = + unsafe { mem::MaybeUninit::::zeroed().assume_init() }; + rsp.reason = req.reason; + rsp.version = VM_EVENT_INTERFACE_VERSION; + rsp.vcpu_id = req.vcpu_id; + rsp.flags = req.flags & VM_EVENT_FLAG_VCPU_PAUSED; + self.xc.put_response(&mut rsp, &mut self.back_ring)?; + } + self.xev.xenevtchn_notify()?; + if flag { + Ok(Some(Event { + vcpu, + kind: event_type, + })) + } else { + Ok(None) + } + } + + fn get_page_access(&self, paddr: u64) -> Result> { + let access = self.xc.get_mem_access(self.domid, paddr >> PAGE_SHIFT)?; + Ok(access.into()) + } + + fn set_page_access(&self, paddr: u64, access: Access) -> Result<(), Box> { + Ok(self + .xc + .set_mem_access(self.domid, access.try_into().unwrap(), paddr >> PAGE_SHIFT)?) + } + + fn toggle_intercept( + &mut self, + _vcpu: u16, + intercept_type: InterceptType, + enabled: bool, + ) -> Result<(), Box> { + match intercept_type { + InterceptType::Cr(micro_cr_type) => { + let xen_cr = match micro_cr_type { + CrType::Cr0 => XenCr::Cr0, + CrType::Cr3 => XenCr::Cr3, + CrType::Cr4 => XenCr::Cr4, + }; + Ok(self + .xc + .monitor_write_ctrlreg(self.domid, xen_cr, enabled, true, true)?) + } + InterceptType::Msr(micro_msr_type) => { + Ok(self + .xc + .monitor_mov_to_msr(self.domid, micro_msr_type, enabled)?) + } + InterceptType::Breakpoint => { + Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) + } + InterceptType::Pagefault => Ok(()), + } + } + fn pause(&mut self) -> Result<(), Box> { debug!("pause"); Ok(self.xc.domain_pause(self.domid)?) @@ -218,6 +374,9 @@ impl Introspectable for Xen { impl Drop for Xen { fn drop(&mut self) { - self.close(); + debug!("Closing Xen driver"); + self.xc + .monitor_disable(self.domid) + .expect("Failed to unmap event ring page"); } }