From 95d43a093c080e672552ddc4e4d2caf9bfe16f1f Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Sat, 25 Jul 2020 11:57:37 +0530 Subject: [PATCH 1/6] event support for xen --- Cargo.toml | 8 ++- examples/cr-events.rs | 15 ++--- examples/regs-dump.rs | 2 +- src/driver/xen.rs | 126 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 129 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f1cc3c5..19a4d154 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,16 +28,20 @@ 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 = "write_register", optional = true } xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } -kvmi = { git = "https://github.com/Wenzel/kvmi", rev = "dd10135a27bb3658d399dfb5477299ca0f4baeac", optional = true } +xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "event-notification"} +xenvmevent-sys = { git = "https://github.com/Wenzel/xenvmevent-sys"} +kvmi = { git = "https://github.com/Wenzel/kvmi", optional = true } +>>>>>>> event support for xen 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 } ntapi = { version = "0.3.3", optional = true } vid-sys = { version = "0.3.0", features = ["deprecated-apis"], optional = true } 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/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/driver/xen.rs b/src/driver/xen.rs index 388c90d0..fc8fa9e1 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,17 +1,32 @@ -use crate::api::{DriverInitParam, Introspectable, Registers, SegmentReg, X86Registers}; -use libc::PROT_READ; use std::error::Error; +use std::mem; + +use crate::api::{ + CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, X86Registers, DriverInitParam, +}; + +use libc::PROT_READ; +use nix::poll::PollFlags; +use nix::poll::{poll, PollFd}; +use std::convert::TryInto; use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; -use xenctrl::XenControl; +use xenctrl::RING_HAS_UNCONSUMED_REQUESTS; +use xenctrl::{XenControl, XenCr, XenEventType}; +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, +}; -// 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 { @@ -33,21 +48,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 { @@ -164,6 +183,86 @@ 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, + }, + _ => unimplemented!(), + }; + 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 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)?) + } + _ => unimplemented!(), + } + } + fn pause(&mut self) -> Result<(), Box> { debug!("pause"); Ok(self.xc.domain_pause(self.domid)?) @@ -177,6 +276,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"); } } From 8df7708b6d42b2156a50db0d797dc4496e9495af Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Fri, 14 Aug 2020 22:55:00 +0530 Subject: [PATCH 2/6] msr event support added --- Cargo.toml | 2 +- src/api.rs | 5 ++--- src/driver/kvm.rs | 3 +-- src/driver/xen.rs | 6 ++++++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 19a4d154..4fbc7d4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ 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/arnabcs17b006/xenctrl", branch = "write_register", optional = true } +xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "msr_events", optional = true } xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "event-notification"} diff --git a/src/api.rs b/src/api.rs index c7de2b13..ce42e816 100644 --- a/src/api.rs +++ b/src/api.rs @@ -274,6 +274,7 @@ pub enum InterceptType { #[repr(C)] #[derive(Debug)] pub enum EventType { +<<<<<<< HEAD ///Cr register interception Cr { ///Type of control register @@ -288,9 +289,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 { diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index 1229db33..f6a8f92e 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -250,8 +250,7 @@ impl Introspectable for Kvm { }, KVMiEventType::Msr { msr_type, new, old } => EventType::Msr { msr_type, - new, - old, + value, }, KVMiEventType::Breakpoint {gpa, insn_len } => EventType::Breakpoint { gpa, diff --git a/src/driver/xen.rs b/src/driver/xen.rs index fc8fa9e1..6173b1b6 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -220,6 +220,7 @@ impl Introspectable for Xen { new, old, }, + XenEventType::Msr { msr_type, value } => EventType::Msr { msr_type, value }, _ => unimplemented!(), }; vcpu = req.vcpu_id.try_into().unwrap(); @@ -259,6 +260,11 @@ impl Introspectable for Xen { .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)?) + } _ => unimplemented!(), } } From 4f1937e68bedc1366082f8c95c8faf9583a2e7fb Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Sat, 15 Aug 2020 00:11:09 +0530 Subject: [PATCH 3/6] breakpoint support added --- Cargo.toml | 2 +- src/api.rs | 1 - src/driver/xen.rs | 6 ++++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4fbc7d4b..30483bc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ 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/arnabcs17b006/xenctrl", branch = "msr_events", optional = true } +xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "breakpoint", optional = true } xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "event-notification"} diff --git a/src/api.rs b/src/api.rs index ce42e816..1f9df9f5 100644 --- a/src/api.rs +++ b/src/api.rs @@ -274,7 +274,6 @@ pub enum InterceptType { #[repr(C)] #[derive(Debug)] pub enum EventType { -<<<<<<< HEAD ///Cr register interception Cr { ///Type of control register diff --git a/src/driver/xen.rs b/src/driver/xen.rs index 6173b1b6..33929201 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -221,6 +221,9 @@ impl Introspectable for Xen { old, }, XenEventType::Msr { msr_type, value } => EventType::Msr { msr_type, value }, + XenEventType::Breakpoint { gpa, insn_len } => { + EventType::Breakpoint { gpa, insn_len } + } _ => unimplemented!(), }; vcpu = req.vcpu_id.try_into().unwrap(); @@ -265,6 +268,9 @@ impl Introspectable for Xen { .xc .monitor_mov_to_msr(self.domid, micro_msr_type, enabled)?) } + InterceptType::Breakpoint => { + Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) + } _ => unimplemented!(), } } From 535e45e5d10951e96fa139881f4cec0f06446fd4 Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Sun, 16 Aug 2020 20:00:57 +0530 Subject: [PATCH 4/6] pagefault event support added --- Cargo.toml | 3 +- examples/mem-events.rs | 104 +++++++++++++++++++++++++++++++++++++++++ src/api.rs | 25 ++++++++++ src/driver/kvm.rs | 2 +- src/driver/xen.rs | 64 ++++++++++++++++++++++--- src/lib.rs | 2 + 6 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 examples/mem-events.rs diff --git a/Cargo.toml b/Cargo.toml index 30483bc6..c8dc3858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ 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/arnabcs17b006/xenctrl", branch = "breakpoint", optional = true } +xenctrl = { git = "https://github.com/cs17b006/xenctrl", branch = "pagefault", optional = true } xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "event-notification"} @@ -42,6 +42,7 @@ ntapi = { version = "0.3.3", optional = true } vid-sys = { version = "0.3.0", features = ["deprecated-apis"], optional = true } cty = "0.2.1" nix = "0.18.0" +bitflags = "1.2.1" [dev-dependencies] ctrlc = "3.1.3" diff --git a/examples/mem-events.rs b/examples/mem-events.rs new file mode 100644 index 00000000..f11c4663 --- /dev/null +++ b/examples/mem-events.rs @@ -0,0 +1,104 @@ +use clap::{App, Arg, ArgMatches}; +use colored::*; +use env_logger; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::time::Instant; + +use microvmi::api::{ + Access, EventReplyType, EventType, InterceptType, Introspectable, PAGE_SHIFT, PAGE_SIZE, +}; + +fn parse_args() -> ArgMatches<'static> { + App::new(file!()) + .version("0.1") + .about("Watches memory VMI events") + .arg(Arg::with_name("vm_name").index(1).required(true)) + .get_matches() +} + +fn toggle_pf_intercept(drv: &mut Box, enabled: bool) { + drv.pause().expect("Failed to pause VM"); + + let intercept = InterceptType::Pagefault; + let status_str = if enabled { "Enabling" } else { "Disabling" }; + println!("{} memory events", status_str); + for vcpu in 0..1 { + drv.toggle_intercept(vcpu, intercept, enabled) + .expect(&format!("Failed to enable page faults")); + } + + drv.resume().expect("Failed to resume VM"); +} + +fn main() { + env_logger::init(); + + 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(); + ctrlc::set_handler(move || { + r.store(false, Ordering::SeqCst); + }) + .expect("Error setting Ctrl-C handler"); + + println!("Initialize Libmicrovmi"); + let mut drv: Box = microvmi::init(domain_name, None); + println!("Listen for memory events..."); + // record elapsed time + let start = Instant::now(); + toggle_pf_intercept(&mut drv, true); + let mut i: u64 = 0; + + //Code snippet to get page fault + let max_addr = drv.get_max_physical_addr().unwrap(); + //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::X) + .expect("failed to set page access"); + } + + while running.load(Ordering::SeqCst) { + let event = drv.listen(1000).expect("Failed to listen for events"); + match event { + Some(ev) => { + let (gva, gpa, pf_access) = match ev.kind { + EventType::Pagefault { gva, gpa, access } => (gva, gpa, access), + _ => panic!("Not pf event"), + }; + let ev_nb_output = format!("{}", i).cyan(); + let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); + let pagefault_output = format!("pagefault occurred!").color("blue"); + println!( + "[{}] {} - {}: gva = 0x{:x} gpa = 0x{:x} access = {:?} ", + ev_nb_output, vcpu_output, pagefault_output, gva, gpa, pf_access + ); + 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, Access::RWX) + .expect("Failed to set page access"); + //drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); + i = i + 1; + } + None => println!("No events yet..."), + } + } + let duration = start.elapsed(); + toggle_pf_intercept(&mut drv, false); + + let ev_per_sec = i as f64 / duration.as_secs_f64(); + println!( + "Caught {} events in {:.2} seconds ({:.2} events/sec)", + i, + duration.as_secs_f64(), + ev_per_sec + ); +} diff --git a/src/api.rs b/src/api.rs index 1f9df9f5..7f3e928d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -4,6 +4,20 @@ use std::ffi::{CStr, IntoStringError}; use crate::capi::DriverInitParamFFI; + +bitflags! { + pub struct Access: u32 { + const R=0b00000001; + const W=0b00000010; + const X=0b00000100; + const NIL=0b00000000; + const RW=Self::R.bits | Self::W.bits; + const WX=Self::W.bits | Self::X.bits; + const RX=Self::R.bits | Self::X.bits; + const RWX=Self::R.bits | Self::W.bits | Self::X.bits; + } +} + ///Represents the available hypervisor VMI drivers supported by libmicrovmi #[repr(C)] #[derive(Debug)] @@ -206,6 +220,16 @@ pub trait Introspectable { unimplemented!(); } + //get page access + fn get_page_access(&self, _paddr: u64) -> Result> { + unimplemented!(); + } + + //set page access + fn set_page_access(&self, _paddr: u64, _access: Access) -> Result<(), Box> { + unimplemented!(); + } + /// Used to pause the VM /// fn pause(&mut self) -> Result<(), Box> { @@ -268,6 +292,7 @@ pub enum InterceptType { Msr(u32), /// Intercept when guest requests an access to a page for which the requested type of access is not granted. For example , guest tries to write on a read only page. Breakpoint, + Pagefault, } /// Various types of events along with their relevant attributes being handled by this driver diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index f6a8f92e..49757942 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -250,7 +250,7 @@ impl Introspectable for Kvm { }, KVMiEventType::Msr { msr_type, new, old } => EventType::Msr { msr_type, - value, + value: new, }, KVMiEventType::Breakpoint {gpa, insn_len } => EventType::Breakpoint { gpa, diff --git a/src/driver/xen.rs b/src/driver/xen.rs index 33929201..78b7b643 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,9 +1,14 @@ -use std::error::Error; -use std::mem; - use crate::api::{ +<<<<<<< HEAD CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, X86Registers, DriverInitParam, +======= + Access, CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, + X86Registers, +>>>>>>> pagefault event support added }; +use std::convert::{From, TryFrom}; +use std::error::Error; +use std::mem; use libc::PROT_READ; use nix::poll::PollFlags; @@ -11,7 +16,7 @@ use nix::poll::{poll, PollFd}; use std::convert::TryInto; use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; use xenctrl::RING_HAS_UNCONSUMED_REQUESTS; -use xenctrl::{XenControl, XenCr, XenEventType}; +use xenctrl::{XenControl, XenCr, XenEventType, XenPageAccess}; use xenevtchn::XenEventChannel; use xenforeignmemory::XenForeignMem; use xenstore::{XBTransaction, Xs, XsOpenFlags}; @@ -19,6 +24,38 @@ 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, + } + } +} + #[derive(Debug)] pub struct Xen { xc: XenControl, @@ -224,7 +261,11 @@ impl Introspectable for Xen { XenEventType::Breakpoint { gpa, insn_len } => { EventType::Breakpoint { gpa, insn_len } } - _ => unimplemented!(), + XenEventType::Pagefault { gva, gpa, access } => EventType::Pagefault { + gva, + gpa, + access: access.into(), + }, }; vcpu = req.vcpu_id.try_into().unwrap(); let mut rsp = @@ -246,6 +287,17 @@ impl Introspectable for Xen { } } + 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, @@ -271,7 +323,7 @@ impl Introspectable for Xen { InterceptType::Breakpoint => { Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) } - _ => unimplemented!(), + InterceptType::Pagefault => Ok(()), } } diff --git a/src/lib.rs b/src/lib.rs index a3fe0978..1a93c4ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,8 @@ mod driver; #[macro_use] extern crate log; +#[macro_use] +extern crate bitflags; use api::Introspectable; use api::{DriverInitParam, DriverType}; From 03e452cbb0de5c4838ffa18ff3f428f28cfe7d5a Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Tue, 18 Aug 2020 13:36:18 +0000 Subject: [PATCH 5/6] singlestep support added --- Cargo.toml | 11 +- examples/mem-events.rs | 8 +- examples/msr-events.rs | 8 +- examples/singlestep-events.rs | 96 +++++++++++ src/api.rs | 16 +- src/driver/xen.rs | 293 +++++++++++++++++++++++++++++++--- src/lib.rs | 32 +++- 7 files changed, 424 insertions(+), 40 deletions(-) create mode 100644 examples/singlestep-events.rs diff --git a/Cargo.toml b/Cargo.toml index c8dc3858..c33a7466 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,13 +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/cs17b006/xenctrl", branch = "pagefault", optional = true } -xenstore = { git = "https://github.com/Wenzel/xenstore", optional = true } -xenforeignmemory = { git = "https://github.com/Wenzel/xenforeignmemory", optional = true } -xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "event-notification"} +xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "mockable_crate", optional = true } +xenstore = { git = "https://github.com/arnabcs17b006/xenstore", branch = "mockable_crate", optional = true } +xenforeignmemory = { git = "https://github.com/arnabcs17b006/xenforeignmemory", branch = "mockable_crate", optional = true } +xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "mockable_crate"} xenvmevent-sys = { git = "https://github.com/Wenzel/xenvmevent-sys"} kvmi = { git = "https://github.com/Wenzel/kvmi", optional = true } ->>>>>>> event support for xen 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 } @@ -43,6 +42,7 @@ vid-sys = { version = "0.3.0", features = ["deprecated-apis"], optional = true } cty = "0.2.1" nix = "0.18.0" bitflags = "1.2.1" +failure = "0.1.7" [dev-dependencies] ctrlc = "3.1.3" @@ -50,3 +50,4 @@ clap = "2.33.0" colored = "1.9.3" mockall = "0.7.1" test-case = "1.0.0" + diff --git a/examples/mem-events.rs b/examples/mem-events.rs index f11c4663..1e4f39e8 100644 --- a/examples/mem-events.rs +++ b/examples/mem-events.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use std::time::Instant; use microvmi::api::{ - Access, EventReplyType, EventType, InterceptType, Introspectable, PAGE_SHIFT, PAGE_SIZE, + Access, DriverInitParam, EventReplyType, EventType, InterceptType, Introspectable, PAGE_SIZE, }; fn parse_args() -> ArgMatches<'static> { @@ -38,6 +38,10 @@ fn main() { let domain_name = matches.value_of("vm_name").unwrap(); + let init_option = matches + .value_of("kvmi_socket") + .map(|socket| DriverInitParam::KVMiSocket(socket.into())); + // set CTRL-C handler let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -47,7 +51,7 @@ fn main() { .expect("Error setting Ctrl-C handler"); println!("Initialize Libmicrovmi"); - let mut drv: Box = microvmi::init(domain_name, None); + let mut drv: Box = microvmi::init(domain_name, None, init_option); println!("Listen for memory events..."); // record elapsed time let start = Instant::now(); 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/singlestep-events.rs b/examples/singlestep-events.rs new file mode 100644 index 00000000..2d21fc36 --- /dev/null +++ b/examples/singlestep-events.rs @@ -0,0 +1,96 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::time::Instant; + +use clap::{App, Arg, ArgMatches}; +use colored::*; +use env_logger; + +use microvmi::api::*; + +fn parse_args() -> ArgMatches<'static> { + App::new(file!()) + .version("0.1") + .about("Watches singlestep VMI events") + .arg(Arg::with_name("vm_name").index(1).required(true)) + .get_matches() +} + +fn toggle_singlestep_interception(drv: &mut Box, enabled: bool) { + drv.pause().expect("Failed to pause VM"); + + let intercept = InterceptType::Singlestep; + let status_str = if enabled { "Enabling" } else { "Disabling" }; + println!("{} singlestep events", status_str); + for vcpu in 0..1 { + drv.toggle_intercept(vcpu, intercept, enabled) + .expect(&format!("Failed to enable singlestep")); + } + + drv.resume().expect("Failed to resume VM"); +} + +fn main() { + env_logger::init(); + + let matches = parse_args(); + + let domain_name = matches.value_of("vm_name").unwrap(); + + let init_option = matches + .value_of("kvmi_socket") + .map(|socket| DriverInitParam::KVMiSocket(socket.into())); + // set CTRL-C handler + let running = Arc::new(AtomicBool::new(true)); + let r = running.clone(); + ctrlc::set_handler(move || { + r.store(false, Ordering::SeqCst); + }) + .expect("Error setting Ctrl-C handler"); + + println!("Initialize Libmicrovmi"); + let mut drv: Box = microvmi::init(domain_name, None, init_option); + + //Enable singlestep interception + toggle_singlestep_interception(&mut drv, true); + + println!("Listen for singlestep events..."); + // record elapsed time + let start = Instant::now(); + // listen + let mut i: u64 = 0; + while running.load(Ordering::SeqCst) { + let event = drv.listen(1000).expect("Failed to listen for events"); + match event { + Some(ev) => { + let gpa = match ev.kind { + EventType::Singlestep { gpa } => (gpa), + _ => panic!("Not singlestep event"), + }; + let ev_nb_output = format!("{}", i).cyan(); + let vcpu_output = format!("VCPU {}", ev.vcpu).yellow(); + let singlestep_output = format!("singlestep occurred!").color("blue"); + println!( + "[{}] {} - {}: gpa = 0x{:x} ", + ev_nb_output, vcpu_output, singlestep_output, gpa + ); + //drv.reply_event(ev, EventReplyType::Continue) + // .expect("Failed to send event reply"); + i = i + 1; + } + None => println!("No events yet..."), + } + } + let duration = start.elapsed(); + + //disable singlestep interception + toggle_singlestep_interception(&mut drv, false); + + let ev_per_sec = i as f64 / duration.as_secs_f64(); + println!( + "Caught {} events in {:.2} seconds ({:.2} events/sec)", + i, + duration.as_secs_f64(), + ev_per_sec + ); +} diff --git a/src/api.rs b/src/api.rs index 7f3e928d..4e6b41d5 100644 --- a/src/api.rs +++ b/src/api.rs @@ -4,7 +4,6 @@ use std::ffi::{CStr, IntoStringError}; use crate::capi::DriverInitParamFFI; - bitflags! { pub struct Access: u32 { const R=0b00000001; @@ -293,6 +292,7 @@ pub enum InterceptType { /// Intercept when guest requests an access to a page for which the requested type of access is not granted. For example , guest tries to write on a read only page. Breakpoint, Pagefault, + Singlestep, } /// Various types of events along with their relevant attributes being handled by this driver @@ -322,6 +322,20 @@ 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, + /// Physical memory address of the guest + gpa: u64, + /// Acsess responsible for thr pagefault + access: Access, + }, + ///Singlestep event + Singlestep { + ///Physical memory address of the guest + gpa: u64, + }, } ///Types of x86 control registers are listed here diff --git a/src/driver/xen.rs b/src/driver/xen.rs index 78b7b643..156f5a5e 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,10 +1,6 @@ use crate::api::{ -<<<<<<< HEAD - CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, X86Registers, DriverInitParam, -======= - Access, CrType, Event, EventType, InterceptType, Introspectable, Registers, SegmentReg, - X86Registers, ->>>>>>> pagefault event support added + Access, CrType, DriverInitParam, Event, EventType, InterceptType, Introspectable, Registers, + SegmentReg, X86Registers, }; use std::convert::{From, TryFrom}; use std::error::Error; @@ -16,10 +12,10 @@ use nix::poll::{poll, PollFd}; use std::convert::TryInto; use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; use xenctrl::RING_HAS_UNCONSUMED_REQUESTS; -use xenctrl::{XenControl, XenCr, XenEventType, XenPageAccess}; -use xenevtchn::XenEventChannel; -use xenforeignmemory::XenForeignMem; -use xenstore::{XBTransaction, Xs, XsOpenFlags}; +use xenctrl::{XenCr, XenEventType, XenIntrospectable, XenPageAccess}; +use xenevtchn::EventChannelSetup; +use xenforeignmemory::XenForeignMemoryIntrospectable; +use xenstore::{XBTransaction, XsIntrospectable, XsOpenFlags}; use xenvmevent_sys::{ vm_event_back_ring, vm_event_response_t, VM_EVENT_FLAG_VCPU_PAUSED, VM_EVENT_INTERFACE_VERSION, }; @@ -57,20 +53,39 @@ impl From for Access { } #[derive(Debug)] -pub struct Xen { - xc: XenControl, - xev: XenEventChannel, - xen_fgn: XenForeignMem, +pub struct Xen< + T: XenIntrospectable, + U: EventChannelSetup, + V: XenForeignMemoryIntrospectable, + W: XsIntrospectable, +> { + xc: T, + xev: U, + xen_fgn: V, + xs: W, dom_name: String, domid: u32, back_ring: vm_event_back_ring, } -impl Xen { - pub fn new(domain_name: &str, _init_option: Option) -> Self { +impl< + T: XenIntrospectable, + U: EventChannelSetup, + V: XenForeignMemoryIntrospectable, + W: XsIntrospectable, + > Xen +{ + pub fn new( + domain_name: &str, + mut xc: T, + mut xev: U, + mut xen_fgn: V, + mut xs: W, + _init_option: Option, + ) -> Result> { debug!("init on {}", domain_name); + xs.init(XsOpenFlags::ReadOnly)?; // find domain name in xenstore - let xs = Xs::new(XsOpenFlags::ReadOnly).unwrap(); let mut found: bool = false; let mut cand_domid = 0; for domid_str in xs.directory(XBTransaction::Null, "/local/domain".to_string()) { @@ -86,27 +101,33 @@ impl Xen { panic!("Cannot find domain {}", domain_name); } - let mut xc = XenControl::new(None, None, 0).unwrap(); + xc.init(None, None, 0)?; 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(); + xev.init(cand_domid, remote_port)?; + xen_fgn.init()?; let xen = Xen { xc, xev, xen_fgn, + xs, dom_name: domain_name.to_string(), domid: cand_domid, back_ring, }; debug!("Initialized {:#?}", xen); - xen + Ok(xen) } } -impl Introspectable for Xen { +impl< + T: XenIntrospectable, + U: EventChannelSetup, + V: XenForeignMemoryIntrospectable, + W: XsIntrospectable, + > Introspectable for Xen +{ fn read_physical(&self, paddr: u64, buf: &mut [u8]) -> Result<(), Box> { let mut cur_paddr: u64; let mut offset: u64 = 0; @@ -266,6 +287,7 @@ impl Introspectable for Xen { gpa, access: access.into(), }, + XenEventType::Singlestep { gpa } => EventType::Singlestep { gpa }, }; vcpu = req.vcpu_id.try_into().unwrap(); let mut rsp = @@ -324,6 +346,7 @@ impl Introspectable for Xen { Ok(self.xc.monitor_software_breakpoint(self.domid, enabled)?) } InterceptType::Pagefault => Ok(()), + InterceptType::Singlestep => Ok(self.xc.monitor_singlestep(self.domid, enabled)?), } } @@ -338,7 +361,13 @@ impl Introspectable for Xen { } } -impl Drop for Xen { +impl< + T: XenIntrospectable, + U: EventChannelSetup, + V: XenForeignMemoryIntrospectable, + W: XsIntrospectable, + > Drop for Xen +{ fn drop(&mut self) { debug!("Closing Xen driver"); self.xc @@ -346,3 +375,219 @@ impl Drop for Xen { .expect("Failed to unmap event ring page"); } } + +#[cfg(test)] +mod tests { + use super::*; + use mockall::mock; + use mockall::predicate::{eq, function}; + use std::fmt::{Debug, Formatter}; + use test_case::test_case; + use xenctrl::{ + hvm_hw_cpu, vm_event_back_ring, vm_event_request_t, vm_event_response_t, vm_event_sring, + xentoollog_logger, XenEventType, XenPageAccess, + }; + use xenevtchn::{evtchn_port_t, xenevtchn_port_or_error_t}; + use xenforeignmemory::XenForeignMem; + use xenstore::{XBTransaction, XsOpenFlags}; + use std::os::raw::{c_uint, c_int}; + + #[test] + fn test_fail_to_create_kvm_driver_if_xencontrol_init_returns_error() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + xencontrol_mock.expect_init().returning(|_, _, _| { + Err(xenctrl::error::XcError::new()) + }); + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| Ok(())); + + let result = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_fail_to_create_kvm_driver_if_xenevtchn_init_returns_error() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); + xenevtchn_mock.expect_init().returning(|_, _| { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + )) + }); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| Ok(())); + + let result = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_fail_to_create_kvm_driver_if_xenforeignmemory_init_returns_error() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| { + Err(failure::Error::new()) + }); + xenstore_mock.expect_init().returning(|_| Ok(())); + + let result = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_fail_to_create_kvm_driver_if_xenstore_init_returns_error() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + )) + }); + let result = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + mock! { + XenControl{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait XenIntrospectable: Debug { + fn init( + &mut self, + logger: Option<&mut xentoollog_logger>, + dombuild_logger: Option<&mut xentoollog_logger>, + open_flags: u32, + ) -> Result<(), xenctrl::error::XcError>; + fn domain_hvm_getcontext_partial(&self, domid: u32, vcpu: u16) -> Result; + fn domain_hvm_setcontext(&self, domid: u32, buffer: *mut c_uint, size: usize) -> Result<(), xenctrl::error::XcError>; + fn domain_hvm_getcontext( + &self, + domid: u32, + vcpu: u16, + ) -> Result<(*mut c_uint, hvm_hw_cpu, u32), xenctrl::error::XcError>; + fn monitor_enable(&mut self, domid: u32) -> Result<(vm_event_sring, vm_event_back_ring, u32), xenctrl::error::XcError>; + fn get_request(&self, back_ring: &mut vm_event_back_ring) -> Result; + fn put_response( + &self, + rsp: &mut vm_event_response_t, + back_ring: &mut vm_event_back_ring, + ) -> Result<(), xenctrl::error::XcError>; + fn get_event_type(&self, req: vm_event_request_t) -> Result; + fn monitor_disable(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn domain_pause(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn domain_unpause(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn monitor_software_breakpoint(&self, domid: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_singlestep(&self, domid: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_mov_to_msr(&self, domid: u32, msr: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_write_ctrlreg( + &self, + domid: u32, + index: XenCr, + enable: bool, + sync: bool, + onchangeonly: bool, + ) -> Result<(), xenctrl::error::XcError>; + fn set_mem_access( + &self, + domid: u32, + access: XenPageAccess, + first_pfn: u64, + ) -> Result<(), xenctrl::error::XcError>; + fn get_mem_access(&self, domid: u32, pfn: u64) -> Result; + fn domain_maximum_gpfn(&self, domid: u32) -> Result; + fn close(&mut self) -> Result<(), xenctrl::error::XcError>; + } + } + + mock! { + XenEventChannel{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait EventChannelSetup: Debug { + fn init(&mut self, domid: u32, evtchn_port: u32) -> Result<(), std::io::Error>; + fn get_bind_port(&self) -> i32; + fn xenevtchn_pending(&self) -> Result; + fn xenevtchn_fd(&self) -> Result; + fn xenevtchn_unmask(&self, port: evtchn_port_t) -> Result<(), std::io::Error>; + fn xenevtchn_notify(&self) -> Result<(), std::io::Error>; + } + } + + mock! { + Xs{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait XsIntrospectable: Debug { + fn init(&mut self, open_type: XsOpenFlags) -> Result<(), std::io::Error>; + fn directory(&self, transaction: XBTransaction, path: String) -> Vec; + fn read(&self, transaction: XBTransaction, path: String) -> String; + fn close(&mut self); + } + } + + mock! { + XenForeignMem{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait XenForeignMemoryIntrospectable: Debug { + fn init(&mut self) -> Result<(), failure::Error>; + fn map(&self, domid: u32, prot: c_int, gfn: u64) -> Result<&mut [u8], failure::Error>; + fn unmap(&self, page: &mut [u8]) -> Result<(), Box>; + fn close(&mut self) -> Result<(), failure::Error>; + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 1a93c4ed..87acefd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,9 @@ mod driver; extern crate log; #[macro_use] extern crate bitflags; +#[macro_use] +extern crate failure; + use api::Introspectable; use api::{DriverInitParam, DriverType}; @@ -21,6 +24,14 @@ use driver::virtualbox::VBox; use driver::xen::Xen; #[cfg(feature = "kvm")] use kvmi::create_kvmi; +#[cfg(feature = "xen")] +use xenctrl::create_xen_control; +#[cfg(feature = "xen")] +use xenevtchn::create_xen_event_channel; +#[cfg(feature = "xen")] +use xenforeignmemory::create_xen_foreignmemory; +#[cfg(feature = "xen")] +use xenstore::create_xen_store; #[allow(unreachable_code)] pub fn init( @@ -45,9 +56,7 @@ pub fn init( Box::new(VBox::new(domain_name, init_option)) as Box } #[cfg(feature = "xen")] - DriverType::Xen => { - Box::new(Xen::new(domain_name, init_option)) as Box - } + DriverType::Xen => create_xen(domain_name, init_option), }, None => { // test Hyper-V @@ -71,7 +80,7 @@ pub fn init( // test Xen #[cfg(feature = "xen")] { - return Box::new(Xen::new(domain_name, init_option)) as Box; + return create_xen(domain_name, init_option); } // return Dummy if no other driver has been compiled Box::new(Dummy::new(domain_name, init_option)) as Box @@ -83,3 +92,18 @@ pub fn init( fn create_kvm(domain_name: &str, init_option: Option) -> Box { Box::new(Kvm::new(domain_name, create_kvmi(), init_option).unwrap()) as Box } + +#[cfg(feature = "xen")] +fn create_xen(domain_name: &str, init_option: Option) -> Box { + Box::new( + Xen::new( + domain_name, + create_xen_control(), + create_xen_event_channel(), + create_xen_foreignmemory(), + create_xen_store(), + init_option, + ) + .unwrap(), + ) as Box +} From c89ed335ffed6def59ce801dceaab435d4f64386 Mon Sep 17 00:00:00 2001 From: arnabcs17b006 Date: Sat, 29 Aug 2020 02:52:36 +0530 Subject: [PATCH 6/6] unit tests added --- Cargo.toml | 6 +- src/api.rs | 47 +- src/driver/kvm.rs | 90 ++- src/driver/xen.rs | 1431 +++++++++++++++++++++++++++++++++++++++++---- src/lib.rs | 1 - 5 files changed, 1431 insertions(+), 144 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c33a7466..3837dc42 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", "xenvmevent-sys", "xenevtchn"] # KVM driver kvm = ["kvmi"] # VirtualBox driver @@ -31,8 +31,8 @@ libc = { version = "0.2.58", optional = true } xenctrl = { git = "https://github.com/arnabcs17b006/xenctrl", branch = "mockable_crate", optional = true } xenstore = { git = "https://github.com/arnabcs17b006/xenstore", branch = "mockable_crate", optional = true } xenforeignmemory = { git = "https://github.com/arnabcs17b006/xenforeignmemory", branch = "mockable_crate", optional = true } -xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "mockable_crate"} -xenvmevent-sys = { git = "https://github.com/Wenzel/xenvmevent-sys"} +xenevtchn = { git = "https://github.com/arnabcs17b006/xenevtchn", branch = "mockable_crate", optional = true} +xenvmevent-sys = { git = "https://github.com/arnabcs17b006/xenvmevent-sys", branch = "derive_default", optional = true} kvmi = { git = "https://github.com/Wenzel/kvmi", optional = true } fdp = { git = "https://github.com/Wenzel/fdp", optional = true } winapi = { version = "0.3.8", features = ["tlhelp32", "winnt", "handleapi", "securitybaseapi"], optional = true } diff --git a/src/api.rs b/src/api.rs index 4e6b41d5..e49adba9 100644 --- a/src/api.rs +++ b/src/api.rs @@ -62,7 +62,7 @@ impl TryInto for DriverInitParamFFI { ///an x86 segment register #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Default)] pub struct SegmentReg { ///Stores the base address of a code segment pub base: u64, @@ -72,9 +72,20 @@ pub struct SegmentReg { pub selector: u16, } +/// x86 System Table Registers +/// (GDTR, IDTR) +#[repr(C)] +#[derive(Debug, Default)] +pub struct SystemTableReg { + /// 32/64 bits linear base address + pub base: u64, + /// 16 bits table limit + pub limit: u16, +} + ///Represents all x86 registers on a specific VCPU #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Default)] pub struct X86Registers { /// 8 byte general purpose register. pub rax: u64, @@ -152,6 +163,8 @@ pub struct X86Registers { pub tr: SegmentReg, /// Local descriptor table register pub ldt: SegmentReg, + pub idt: SystemTableReg, + pub gdt: SystemTableReg, } #[repr(C)] @@ -209,23 +222,32 @@ pub trait Introspectable { unimplemented!(); } - /// Write register values + ///get page access /// /// # Arguments - /// * 'vcpu' - vcpu id for which the value of registers are to be set - /// * 'reg' - Registers enum having values to be set + /// * 'paddr' - physical address of the page whose access we want to know. /// - fn write_registers(&self, _vcpu: u16, _reg: Registers) -> Result<(), Box> { + fn get_page_access(&self, _paddr: u64) -> Result> { unimplemented!(); } - //get page access - fn get_page_access(&self, _paddr: u64) -> Result> { + ///set page access + /// + /// # Arguments + /// * 'paddr' - physical address of the page whose access we want to set + /// * 'access' - access flags to be set on the given page + /// + fn set_page_access(&self, _paddr: u64, _access: Access) -> Result<(), Box> { unimplemented!(); } - //set page access - fn set_page_access(&self, _paddr: u64, _access: Access) -> Result<(), Box> { + /// Write register values + /// + /// # Arguments + /// * 'vcpu' - vcpu id for which the value of registers are to be set + /// * 'reg' - Registers enum having values to be set + /// + fn write_registers(&self, _vcpu: u16, _reg: Registers) -> Result<(), Box> { unimplemented!(); } @@ -297,7 +319,7 @@ pub enum InterceptType { /// Various types of events along with their relevant attributes being handled by this driver #[repr(C)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum EventType { ///Cr register interception Cr { @@ -322,7 +344,6 @@ 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, @@ -340,7 +361,7 @@ pub enum EventType { ///Types of x86 control registers are listed here #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum CrType { ///Has various control flags that modify the basic operation of the processor. Cr0, diff --git a/src/driver/kvm.rs b/src/driver/kvm.rs index 49757942..2ff26be4 100644 --- a/src/driver/kvm.rs +++ b/src/driver/kvm.rs @@ -1,17 +1,51 @@ use kvmi::{ - kvm_regs, kvm_segment, KVMIntrospectable, KVMiCr, KVMiEvent, KVMiEventReply, KVMiEventType, - KVMiInterceptType, + kvm_dtable, kvm_regs, kvm_segment, KVMIntrospectable, KVMiCr, KVMiEvent, KVMiEventReply, + KVMiEventType, KVMiInterceptType, KVMiPageAccess, }; +use std::convert::From; +use std::convert::TryFrom; use std::convert::TryInto; use std::error::Error; use std::mem; use std::vec::Vec; use crate::api::{ - CrType, DriverInitParam, Event, EventReplyType, EventType, InterceptType, Introspectable, - Registers, SegmentReg, X86Registers, PAGE_SHIFT, + Access, CrType, DriverInitParam, Event, EventReplyType, EventType, InterceptType, + Introspectable, Registers, SegmentReg, SystemTableReg, X86Registers, PAGE_SHIFT, }; +impl TryFrom for KVMiPageAccess { + type Error = &'static str; + fn try_from(access: Access) -> Result { + match access { + Access::NIL => Ok(KVMiPageAccess::NIL), + Access::R => Ok(KVMiPageAccess::R), + Access::W => Ok(KVMiPageAccess::W), + Access::RW => Ok(KVMiPageAccess::RW), + Access::X => Ok(KVMiPageAccess::X), + Access::RX => Ok(KVMiPageAccess::RX), + Access::WX => Ok(KVMiPageAccess::WX), + Access::RWX => Ok(KVMiPageAccess::RWX), + _ => Err("invalid access value"), + } + } +} + +impl From for Access { + fn from(access: KVMiPageAccess) -> Self { + match access { + KVMiPageAccess::NIL => Access::NIL, + KVMiPageAccess::R => Access::R, + KVMiPageAccess::W => Access::W, + KVMiPageAccess::RW => Access::RW, + KVMiPageAccess::X => Access::X, + KVMiPageAccess::RX => Access::RX, + KVMiPageAccess::WX => Access::WX, + KVMiPageAccess::RWX => Access::RWX, + } + } +} + impl From for SegmentReg { fn from(segment: kvm_segment) -> Self { SegmentReg { @@ -22,6 +56,15 @@ impl From for SegmentReg { } } +impl From for SystemTableReg { + fn from(dtable: kvm_dtable) -> Self { + SystemTableReg { + base: dtable.base, + limit: dtable.limit, + } + } +} + impl From for kvm_regs { fn from(register: X86Registers) -> Self { kvm_regs { @@ -86,7 +129,7 @@ impl Kvm { .control_events(vcpu, KVMiInterceptType::Msr, true) .unwrap(); kvm.kvmi - .control_events(vcpu, KVMiInterceptType::Breakpoint, true) + .control_events(vcpu, KVMiInterceptType::Pagefault, true) .unwrap(); } @@ -155,6 +198,8 @@ impl Introspectable for Kvm { ss: sregs.ss.into(), tr: sregs.tr.into(), ldt: sregs.ldt.into(), + idt: sregs.idt.into(), + gdt: sregs.gdt.into(), })) } @@ -167,6 +212,17 @@ impl Introspectable for Kvm { Ok(()) } + fn get_page_access(&self, paddr: u64) -> Result> { + let access = self.kvmi.get_page_access(paddr).unwrap(); + Ok(access.try_into().unwrap()) + } + + fn set_page_access(&self, paddr: u64, access: Access) -> Result<(), Box> { + self.kvmi + .set_page_access(paddr, access.try_into().unwrap())?; + Ok(()) + } + fn pause(&mut self) -> Result<(), Box> { debug!("pause"); // already paused ? @@ -228,6 +284,12 @@ impl Introspectable for Kvm { .kvmi .control_events(vcpu, KVMiInterceptType::Breakpoint, enabled)?) } + InterceptType::Pagefault => { + Ok(self + .kvmi + .control_events(vcpu, KVMiInterceptType::Pagefault, enabled)?) + } + _ => unimplemented!(), } } @@ -248,7 +310,7 @@ impl Introspectable for Kvm { new, old, }, - KVMiEventType::Msr { msr_type, new, old } => EventType::Msr { + KVMiEventType::Msr { msr_type, new, old: _ } => EventType::Msr { msr_type, value: new, }, @@ -256,8 +318,12 @@ impl Introspectable for Kvm { gpa, insn_len, }, + KVMiEventType::Pagefault {gva, gpa, access, view: _} => EventType::Pagefault { + gva, + gpa, + access: access.into(), + }, KVMiEventType::PauseVCPU => panic!("Unexpected PauseVCPU event. It should have been popped by resume VM. (Did you forget to resume your VM ?)"), - _ => unimplemented!(), }; let vcpu = kvmi_event.vcpu; @@ -299,7 +365,7 @@ impl Drop for Kvm { .control_events(vcpu, KVMiInterceptType::Msr, false) .unwrap(); self.kvmi - .control_events(vcpu, KVMiInterceptType::Breakpoint, false) + .control_events(vcpu, KVMiInterceptType::Pagefault, false) .unwrap(); } } @@ -383,7 +449,7 @@ mod tests { .expect_control_events() .with( eq(vcpu as u16), - function(|x| matches!(x, KVMiInterceptType::Breakpoint)), + function(|x| matches!(x, KVMiInterceptType::Pagefault)), eq(true), ) .times(1) @@ -392,7 +458,7 @@ mod tests { .expect_control_events() .with( eq(vcpu as u16), - function(|x| matches!(x, KVMiInterceptType::Breakpoint)), + function(|x| matches!(x, KVMiInterceptType::Pagefault)), eq(false), ) .times(1) @@ -425,8 +491,8 @@ mod tests { fn control_msr(&self, vcpu: u16, reg: u32, enabled: bool) -> Result<(), std::io::Error>; fn read_physical(&self, gpa: u64, buffer: &mut [u8]) -> Result<(), std::io::Error>; fn write_physical(&self, gpa: u64, buffer: &[u8]) -> Result<(), std::io::Error>; - fn get_page_access(&self, gpa: u64) -> Result; - fn set_page_access(&self, gpa: u64, access: u8) -> Result<(), std::io::Error>; + fn get_page_access(&self, gpa: u64) -> Result; + fn set_page_access(&self, gpa: u64, access: KVMiPageAccess) -> Result<(), std::io::Error>; fn pause(&self) -> Result<(), std::io::Error>; fn get_vcpu_count(&self) -> Result; fn get_registers(&self, vcpu: u16) -> Result<(kvm_regs, kvm_sregs, KvmMsrs), std::io::Error>; diff --git a/src/driver/xen.rs b/src/driver/xen.rs index 156f5a5e..8c087976 100644 --- a/src/driver/xen.rs +++ b/src/driver/xen.rs @@ -1,12 +1,12 @@ use crate::api::{ Access, CrType, DriverInitParam, Event, EventType, InterceptType, Introspectable, Registers, - SegmentReg, X86Registers, + SegmentReg, SystemTableReg, X86Registers, }; use std::convert::{From, TryFrom}; use std::error::Error; use std::mem; -use libc::PROT_READ; +use libc::{PROT_READ, PROT_WRITE}; use nix::poll::PollFlags; use nix::poll::{poll, PollFd}; use std::convert::TryInto; @@ -154,7 +154,38 @@ impl< count_mut -= read_len; buf_offset += read_len; // unmap page - self.xen_fgn.unmap(page).unwrap(); + self.xen_fgn.unmap(page)?; + } + Ok(()) + } + + fn write_physical(&self, paddr: u64, buf: &mut [u8]) -> Result<(), Box> { + let mut phys_address: u64; + let mut offset: u64; + let mut count_mut: u64 = buf.len() as u64; + let mut buf_offset: u64 = 0; + while count_mut > 0 { + // compute new paddr + phys_address = paddr + buf_offset; + // get the current pfn + let pfn = phys_address >> PAGE_SHIFT; + offset = u64::from(PAGE_SIZE - 1) & phys_address; + // map pfn + let page = self.xen_fgn.map(self.domid, PROT_WRITE, pfn)?; + // determine how much we can write + let write_len = if (offset + count_mut as u64) > u64::from(PAGE_SIZE) { + u64::from(PAGE_SIZE) - offset + } else { + count_mut as u64 + }; + + // do the write + page[offset as usize..write_len as usize].copy_from_slice(&buf[buf_offset as usize..]); + // update loop variables + count_mut -= write_len; + buf_offset += write_len; + // unmap page + self.xen_fgn.unmap(page)?; } Ok(()) } @@ -189,58 +220,120 @@ impl< cr0: hvm_cpu.cr0, cr3: hvm_cpu.cr3, cr4: hvm_cpu.cr4, - cr2: 0, - sysenter_cs: 0, - sysenter_esp: 0, - sysenter_eip: 0, - msr_efer: 0, - msr_star: 0, - msr_lstar: 0, - efer: 0, - apic_base: 0, + cr2: hvm_cpu.cr2, + sysenter_cs: hvm_cpu.sysenter_cs, + sysenter_esp: hvm_cpu.sysenter_esp, + sysenter_eip: hvm_cpu.sysenter_eip, + msr_efer: hvm_cpu.msr_efer, + msr_star: hvm_cpu.msr_star, + msr_lstar: hvm_cpu.msr_lstar, cs: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.cs_base, + limit: hvm_cpu.cs_limit, + selector: hvm_cpu.cs_sel.try_into().unwrap(), }, ds: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.ds_base, + limit: hvm_cpu.ds_limit, + selector: hvm_cpu.ds_sel.try_into().unwrap(), }, es: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.es_base, + limit: hvm_cpu.es_limit, + selector: hvm_cpu.es_sel.try_into().unwrap(), }, fs: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.fs_base, + limit: hvm_cpu.fs_limit, + selector: hvm_cpu.fs_sel.try_into().unwrap(), }, gs: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.gs_base, + limit: hvm_cpu.gs_limit, + selector: hvm_cpu.gs_sel.try_into().unwrap(), }, ss: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.ss_base, + limit: hvm_cpu.ss_limit, + selector: hvm_cpu.ss_sel.try_into().unwrap(), }, tr: SegmentReg { - base: 0, - limit: 0, - selector: 0, + base: hvm_cpu.tr_base, + limit: hvm_cpu.tr_limit, + selector: hvm_cpu.tr_sel.try_into().unwrap(), }, - ldt: SegmentReg { - base: 0, - limit: 0, - selector: 0, + idt: SystemTableReg { + base: hvm_cpu.idtr_base, + limit: hvm_cpu.idtr_limit as u16, }, + gdt: SystemTableReg { + base: hvm_cpu.gdtr_base, + limit: hvm_cpu.gdtr_limit as u16, + }, + ..Default::default() })) } + fn write_registers(&self, vcpu: u16, reg: Registers) -> Result<(), Box> { + let (buffer, mut cpu, size) = self.xc.domain_hvm_getcontext(self.domid, vcpu)?; + match reg { + Registers::X86(x86_registers) => { + cpu.rax = x86_registers.rax; + cpu.rbx = x86_registers.rbx; + cpu.rcx = x86_registers.rcx; + cpu.rdx = x86_registers.rdx; + cpu.rsi = x86_registers.rsi; + cpu.rdi = x86_registers.rdi; + cpu.rsp = x86_registers.rsp; + cpu.rbp = x86_registers.rbp; + cpu.r8 = x86_registers.r8; + cpu.r9 = x86_registers.r9; + cpu.r10 = x86_registers.r10; + cpu.r11 = x86_registers.r11; + cpu.r12 = x86_registers.r12; + cpu.r13 = x86_registers.r13; + cpu.r14 = x86_registers.r14; + cpu.r15 = x86_registers.r15; + cpu.rip = x86_registers.rip; + cpu.rflags = x86_registers.rflags; + cpu.cr0 = x86_registers.cr0; + cpu.cr2 = x86_registers.cr2; + cpu.cr3 = x86_registers.cr3; + cpu.cr4 = x86_registers.cr4; + cpu.sysenter_cs = x86_registers.sysenter_cs; + cpu.sysenter_esp = x86_registers.sysenter_esp; + cpu.sysenter_eip = x86_registers.sysenter_eip; + cpu.msr_star = x86_registers.msr_star; + cpu.msr_lstar = x86_registers.msr_lstar; + cpu.msr_efer = x86_registers.msr_efer; + cpu.cs_base = x86_registers.cs.base; + cpu.ds_base = x86_registers.ds.base; + cpu.es_base = x86_registers.es.base; + cpu.fs_base = x86_registers.fs.base; + cpu.gs_base = x86_registers.gs.base; + cpu.ss_base = x86_registers.ss.base; + cpu.tr_base = x86_registers.tr.base; + cpu.cs_limit = x86_registers.cs.limit; + cpu.ds_limit = x86_registers.ds.limit; + cpu.es_limit = x86_registers.es.limit; + cpu.fs_limit = x86_registers.fs.limit; + cpu.gs_limit = x86_registers.gs.limit; + cpu.ss_limit = x86_registers.ss.limit; + cpu.tr_limit = x86_registers.tr.limit; + cpu.cs_sel = x86_registers.cs.selector.try_into().unwrap(); + cpu.ds_sel = x86_registers.ds.selector.try_into().unwrap(); + cpu.es_sel = x86_registers.es.selector.try_into().unwrap(); + cpu.fs_sel = x86_registers.fs.selector.try_into().unwrap(); + cpu.gs_sel = x86_registers.gs.selector.try_into().unwrap(); + cpu.ss_sel = x86_registers.ss.selector.try_into().unwrap(); + cpu.tr_sel = x86_registers.tr.selector.try_into().unwrap(); + } + } + self.xc + .domain_hvm_setcontext(self.domid, buffer, size.try_into().unwrap())?; + Ok(()) + } + fn listen(&mut self, timeout: u32) -> Result, Box> { let fd = self.xev.xenevtchn_fd()?; let fd_struct = PollFd::new(fd, PollFlags::POLLIN | PollFlags::POLLERR); @@ -267,7 +360,7 @@ impl< if req.version != VM_EVENT_INTERFACE_VERSION { panic!("version mismatch"); } - let xen_event_type = (self.xc.get_event_type(req)).unwrap(); + let xen_event_type = (self.xc.get_event_type(req))?; event_type = match xen_event_type { XenEventType::Cr { cr_type, new, old } => EventType::Cr { cr_type: match cr_type { @@ -380,33 +473,44 @@ impl< mod tests { use super::*; use mockall::mock; - use mockall::predicate::{eq, function}; + use mockall::predicate::eq; use std::fmt::{Debug, Formatter}; - use test_case::test_case; + use std::os::raw::{c_int, c_uint}; + use xenctrl::consts::{PAGE_SHIFT, PAGE_SIZE}; use xenctrl::{ hvm_hw_cpu, vm_event_back_ring, vm_event_request_t, vm_event_response_t, vm_event_sring, - xentoollog_logger, XenEventType, XenPageAccess, + xentoollog_logger, XenCr, XenEventType, XenPageAccess, }; use xenevtchn::{evtchn_port_t, xenevtchn_port_or_error_t}; - use xenforeignmemory::XenForeignMem; use xenstore::{XBTransaction, XsOpenFlags}; - use std::os::raw::{c_uint, c_int}; #[test] - fn test_fail_to_create_kvm_driver_if_xencontrol_init_returns_error() { + fn test_fail_to_create_xen_driver_if_xencontrol_init_returns_error() { let mut xencontrol_mock = MockXenControl::default(); let mut xenevtchn_mock = MockXenEventChannel::default(); let mut xenforeignmemory_mock = MockXenForeignMem::default(); let mut xenstore_mock = MockXs::default(); - xencontrol_mock.expect_init().returning(|_, _, _| { - Err(xenctrl::error::XcError::new()) - }); + xencontrol_mock + .expect_init() + .returning(|_, _, _| Err(xenctrl::error::XcError::new("some error"))); xenevtchn_mock.expect_init().returning(|_, _| Ok(())); xenforeignmemory_mock.expect_init().returning(|| Ok(())); xenstore_mock.expect_init().returning(|_| Ok(())); + let mut vec: Vec = Vec::new(); + let domain_name = "some_vm"; + let dom0 = "0"; + let string_dir: String = String::from(dom0); + let string_read: String = String::from(domain_name); + vec.push(string_dir); + xenstore_mock + .expect_directory() + .return_once(move |_, _| vec); + xenstore_mock + .expect_read() + .return_once(move |_, _| string_read); let result = Xen::new( - "some_vm", + &domain_name, xencontrol_mock, xenevtchn_mock, xenforeignmemory_mock, @@ -418,20 +522,38 @@ mod tests { } #[test] - fn test_fail_to_create_kvm_driver_if_xenevtchn_init_returns_error() { + fn test_fail_to_create_xen_driver_if_xenforeignmemory_init_returns_error() { let mut xencontrol_mock = MockXenControl::default(); let mut xenevtchn_mock = MockXenEventChannel::default(); let mut xenforeignmemory_mock = MockXenForeignMem::default(); let mut xenstore_mock = MockXs::default(); xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); - xenevtchn_mock.expect_init().returning(|_, _| { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - "something went wrong", - )) + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| { + Err(failure::Error::from_boxed_compat(Box::new( + std::io::Error::new(std::io::ErrorKind::Other, "something went wrong"), + ))) }); - xenforeignmemory_mock.expect_init().returning(|| Ok(())); xenstore_mock.expect_init().returning(|_| Ok(())); + let mut vec: Vec = Vec::new(); + let domain_name = "some_vm"; + let dom0 = "0"; + let string_dir: String = String::from(dom0); + vec.push(string_dir); + let string_read: String = String::from(domain_name); + xenstore_mock + .expect_directory() + .return_once(move |_, _| vec); + xenstore_mock + .expect_read() + .return_once(move |_, _| string_read); + xencontrol_mock + .expect_monitor_enable() + .return_once(move |_| { + let sring: vm_event_sring = Default::default(); + let bring: vm_event_back_ring = Default::default(); + Ok((sring, bring, 0)) + }); let result = Xen::new( "some_vm", @@ -446,17 +568,20 @@ mod tests { } #[test] - fn test_fail_to_create_kvm_driver_if_xenforeignmemory_init_returns_error() { + fn test_fail_to_create_xen_driver_if_xenstore_init_returns_error() { let mut xencontrol_mock = MockXenControl::default(); let mut xenevtchn_mock = MockXenEventChannel::default(); let mut xenforeignmemory_mock = MockXenForeignMem::default(); let mut xenstore_mock = MockXs::default(); xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); xenevtchn_mock.expect_init().returning(|_, _| Ok(())); - xenforeignmemory_mock.expect_init().returning(|| { - Err(failure::Error::new()) + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + )) }); - xenstore_mock.expect_init().returning(|_| Ok(())); let result = Xen::new( "some_vm", @@ -471,22 +596,42 @@ mod tests { } #[test] - fn test_fail_to_create_kvm_driver_if_xenstore_init_returns_error() { + fn test_fail_to_create_xen_driver_if_xenevtchn_init_returns_error() { let mut xencontrol_mock = MockXenControl::default(); let mut xenevtchn_mock = MockXenEventChannel::default(); let mut xenforeignmemory_mock = MockXenForeignMem::default(); let mut xenstore_mock = MockXs::default(); xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); - xenevtchn_mock.expect_init().returning(|_, _| Ok(())); - xenforeignmemory_mock.expect_init().returning(|| Ok(())); - xenstore_mock.expect_init().returning(|_| { + xenevtchn_mock.expect_init().returning(|_, _| { Err(std::io::Error::new( std::io::ErrorKind::Other, "something went wrong", )) }); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| Ok(())); + let mut vec: Vec = Vec::new(); + let domain_name = "some_vm"; + let dom0 = "0"; + let string_dir: String = String::from(dom0); + let string_read: String = String::from(domain_name); + vec.push(string_dir); + xenstore_mock + .expect_directory() + .return_once(move |_, _| vec); + xenstore_mock + .expect_read() + .return_once(move |_, _| string_read); + xencontrol_mock + .expect_monitor_enable() + .return_once(move |_| { + let sring: vm_event_sring = Default::default(); + let bring: vm_event_back_ring = Default::default(); + Ok((sring, bring, 0)) + }); + let result = Xen::new( - "some_vm", + &domain_name, xencontrol_mock, xenevtchn_mock, xenforeignmemory_mock, @@ -497,60 +642,1116 @@ mod tests { assert!(result.is_err(), "Expected error, got ok instead!"); } - mock! { - XenControl{} - trait Debug { - fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + fn setup( + xencontrol_mock: &mut MockXenControl, + xenevtchn_mock: &mut MockXenEventChannel, + xenforeignmemory_mock: &mut MockXenForeignMem, + xenstore_mock: &mut MockXs, + ) { + xencontrol_mock.expect_init().returning(|_, _, _| Ok(())); + xenevtchn_mock.expect_init().returning(|_, _| Ok(())); + xenforeignmemory_mock.expect_init().returning(|| Ok(())); + xenstore_mock.expect_init().returning(|_| Ok(())); + let mut vec: Vec = Vec::new(); + let domain_name = "some_vm"; + let dom0 = "0"; + let string_dir: String = String::from(dom0); + let string_read: String = String::from(domain_name); + vec.push(string_dir); + xenstore_mock + .expect_directory() + .return_once(move |_, _| vec); + xenstore_mock + .expect_read() + .return_once(move |_, _| string_read); + xencontrol_mock + .expect_monitor_enable() + .return_once(move |_| { + let sring: vm_event_sring = Default::default(); + let bring: vm_event_back_ring = Default::default(); + Ok((sring, bring, 0)) + }); + xencontrol_mock + .expect_monitor_disable() + .return_once(|_| Ok(())); + } + + #[test] + fn test_xen_driver_created_successfully() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + + let result = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ); + + assert!(result.is_ok(), "Expected ok, got error instead!"); + } + + #[test] + fn test_read_physical_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + static mut BUF: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe { + xenforeignmemory_mock + .expect_map() + .return_once(move |_, _, _| Ok(&mut BUF)); } - trait XenIntrospectable: Debug { - fn init( - &mut self, - logger: Option<&mut xentoollog_logger>, - dombuild_logger: Option<&mut xentoollog_logger>, - open_flags: u32, - ) -> Result<(), xenctrl::error::XcError>; - fn domain_hvm_getcontext_partial(&self, domid: u32, vcpu: u16) -> Result; - fn domain_hvm_setcontext(&self, domid: u32, buffer: *mut c_uint, size: usize) -> Result<(), xenctrl::error::XcError>; - fn domain_hvm_getcontext( - &self, - domid: u32, - vcpu: u16, - ) -> Result<(*mut c_uint, hvm_hw_cpu, u32), xenctrl::error::XcError>; - fn monitor_enable(&mut self, domid: u32) -> Result<(vm_event_sring, vm_event_back_ring, u32), xenctrl::error::XcError>; - fn get_request(&self, back_ring: &mut vm_event_back_ring) -> Result; - fn put_response( - &self, - rsp: &mut vm_event_response_t, - back_ring: &mut vm_event_back_ring, - ) -> Result<(), xenctrl::error::XcError>; - fn get_event_type(&self, req: vm_event_request_t) -> Result; - fn monitor_disable(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; - fn domain_pause(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; - fn domain_unpause(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; - fn monitor_software_breakpoint(&self, domid: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; - fn monitor_singlestep(&self, domid: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; - fn monitor_mov_to_msr(&self, domid: u32, msr: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; - fn monitor_write_ctrlreg( - &self, - domid: u32, - index: XenCr, - enable: bool, - sync: bool, - onchangeonly: bool, - ) -> Result<(), xenctrl::error::XcError>; - fn set_mem_access( - &self, - domid: u32, - access: XenPageAccess, - first_pfn: u64, - ) -> Result<(), xenctrl::error::XcError>; - fn get_mem_access(&self, domid: u32, pfn: u64) -> Result; - fn domain_maximum_gpfn(&self, domid: u32) -> Result; - fn close(&mut self) -> Result<(), xenctrl::error::XcError>; + xenforeignmemory_mock.expect_unmap().returning(|_| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.read_physical(paddr, &mut buffer); + + assert!(result.is_ok(), "Expected ok, got error instead!"); + } + + #[test] + fn test_read_physical_fails_if_xen_foreignmemory_map_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xenforeignmemory_mock.expect_map().returning(|_, _, _| { + Err(failure::Error::from_boxed_compat(Box::new( + std::io::Error::new(std::io::ErrorKind::Other, "something went wrong"), + ))) + }); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.read_physical(paddr, &mut buffer); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_read_physical_fails_if_xen_foreignmemory_unmap_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + static mut BUF: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe { + xenforeignmemory_mock + .expect_map() + .return_once(move |_, _, _| Ok(&mut BUF)); } + xenforeignmemory_mock.expect_unmap().returning(|_| { + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + ))) + }); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.read_physical(paddr, &mut buffer); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_read_registers_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let cpu: hvm_hw_cpu = Default::default(); + xencontrol_mock + .expect_domain_hvm_getcontext_partial() + .returning(move |_, _| Ok(cpu)); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.read_registers(vcpu); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_read_registers_fails_if_domain_hvm_getcontext_partial_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_hvm_getcontext_partial() + .returning(move |_, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.read_registers(vcpu); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_write_registers_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let cpu: hvm_hw_cpu = Default::default(); + let size = 1024; + xencontrol_mock + .expect_domain_hvm_getcontext() + .return_once(move |_, _| { + let buf: *mut c_uint = std::ptr::null_mut(); + Ok((buf, cpu, size)) + }); + xencontrol_mock + .expect_domain_hvm_setcontext() + .returning(move |_, _, _| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + let x86_register_struct: X86Registers = Default::default(); + + let result = xen.write_registers(vcpu, Registers::X86(x86_register_struct)); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_write_registers_fails_if_domain_hvm_getcontext_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_hvm_getcontext() + .returning(move |_, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + let x86_register_struct: X86Registers = Default::default(); + + let result = xen.write_registers(vcpu, Registers::X86(x86_register_struct)); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_write_registers_fails_if_domain_hvm_setcontext_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let cpu: hvm_hw_cpu = Default::default(); + let size = 1024; + xencontrol_mock + .expect_domain_hvm_getcontext() + .return_once(move |_, _| { + let buf: *mut c_uint = std::ptr::null_mut(); + Ok((buf, cpu, size)) + }); + xencontrol_mock + .expect_domain_hvm_setcontext() + .returning(move |_, _, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + let x86_register_struct: X86Registers = Default::default(); + + let result = xen.write_registers(vcpu, Registers::X86(x86_register_struct)); + + assert!(result.is_err(), "Expected error, got ok instead"); } - mock! { + #[test] + fn test_write_physical_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + static mut BUF: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe { + xenforeignmemory_mock + .expect_map() + .return_once(move |_, _, _| Ok(&mut BUF)); + } + xenforeignmemory_mock.expect_unmap().returning(|_| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.write_physical(paddr, &mut buffer); + + assert!(result.is_ok(), "Expected ok, got error instead!"); + } + + /*#[test] + fn test_write_physical_fails_if_xen_foreignmemory_map_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup(&mut xencontrol_mock, &mut xenevtchn_mock, &mut xenforeignmemory_mock, &mut xenstore_mock); + static mut buf: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe {xenforeignmemory_mock.expect_map().return_once(move |_, _, _| Ok(&mut buf));} + xenforeignmemory_mock.expect_unmap().returning(|_| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ).expect("Failed to create driver"); + let paddr =0; + let mut buffer = [0;PAGE_SIZE as usize]; + let result = xen.write_physical(paddr, &mut buffer); + assert!(result.is_ok(), "Expected ok, got error instead!"); + }*/ + + #[test] + fn test_write_physical_fails_if_xen_foreignmemory_unmap_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + static mut BUF: [u8; PAGE_SIZE as usize] = [0; PAGE_SIZE as usize]; + unsafe { + xenforeignmemory_mock + .expect_map() + .return_once(move |_, _, _| Ok(&mut BUF)); + } + xenforeignmemory_mock.expect_unmap().returning(|_| { + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "something went wrong", + ))) + }); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + let mut buffer = [0; PAGE_SIZE as usize]; + + let result = xen.write_physical(paddr, &mut buffer); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn get_maximum_physical_address_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let max_gfn = 1; + xencontrol_mock + .expect_domain_maximum_gpfn() + .returning(move |_| Ok(max_gfn)); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let max_addr = xen.get_max_physical_addr().unwrap(); + + assert_eq!(max_addr, max_gfn << PAGE_SHIFT); + } + + #[test] + fn get_maximum_physical_address_fails_if_xenctrl_domain_maximum_gpfn_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_maximum_gpfn() + .returning(|_| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.get_max_physical_addr(); + + assert!(result.is_err(), "Expected error, got ok instead!"); + } + + #[test] + fn test_pause_domain_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock.expect_domain_pause().returning(|_| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.pause(); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_pause_domain_fails_if_xencontrol_domain_pause_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_pause() + .returning(|_| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.pause(); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_resume_domain_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_unpause() + .returning(|_| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.resume(); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_resume_domain_fails_if_xencontrol_domain_unpause_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_domain_unpause() + .returning(|_| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.resume(); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_get_page_access_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_get_mem_access() + .returning(move |_, _| Ok(XenPageAccess::R)); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + + let access = xen.get_page_access(paddr).unwrap(); + + assert_eq!(Access::R, access); + } + + #[test] + fn test_get_page_access_fails_if_xencontrol_get_mem_access_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_get_mem_access() + .returning(|_, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + + let result = xen.get_page_access(paddr); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_set_page_access_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_set_mem_access() + .returning(move |_, _, _| Ok(())); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + + let result = xen.set_page_access(paddr, Access::R); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn test_set_page_access_fails_if_xencontrol_set_mem_access_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_set_mem_access() + .returning(|_, _, _| Err(xenctrl::error::XcError::new("some error"))); + let xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let paddr = 0; + + let result = xen.set_page_access(paddr, Access::R); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn test_listen_events_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + let fd = 1; + xenevtchn_mock + .expect_xenevtchn_fd() + .return_once(move || Ok(fd)); + let port = 56; + xenevtchn_mock + .expect_xenevtchn_pending() + .return_once(move || Ok(port)); + xenevtchn_mock + .expect_xenevtchn_unmask() + .with(eq(port as u32)) + .returning(|_| Ok(())); + xenevtchn_mock + .expect_get_bind_port() + .return_once(move || port); + let request: vm_event_request_t = Default::default(); + xencontrol_mock + .expect_get_request() + .return_once(move |_| Ok(request)); + let cr_type = XenCr::Cr3; + let new = 1; + let old = 2; + xencontrol_mock + .expect_get_event_type() + .return_once(move |_| Ok(XenEventType::Cr { cr_type, new, old })); + xencontrol_mock + .expect_put_response() + .returning(|_, _| Ok(())); + xenevtchn_mock + .expect_xenevtchn_notify() + .returning(|| Ok(())); + let timeout = 1000; + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + + let result = xen.listen(timeout); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_cr_intercept_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_write_ctrlreg() + .returning(|_, _, _, _, _| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Cr(CrType::Cr3), true); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_cr_intercept_fails_if_monitor_write_ctrlreg_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_write_ctrlreg() + .returning(|_, _, _, _, _| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Cr(CrType::Cr3), true); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn toggle_msr_intercept_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_mov_to_msr() + .returning(|_, _, _| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Msr(0x175), true); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_msr_intercept_fails_if_monitor_write_ctrlreg_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_mov_to_msr() + .returning(|_, _, _| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Msr(0x175), true); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn toggle_singlestep_intercept_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_singlestep() + .returning(|_, _| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Singlestep, true); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_singlestep_intercept_fails_if_monitor_write_ctrlreg_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_singlestep() + .returning(|_, _| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Singlestep, true); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + #[test] + fn toggle_breakpoint_intercept_succeeds() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_software_breakpoint() + .returning(|_, _| Ok(())); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Breakpoint, true); + + assert!(result.is_ok(), "Expected ok, got error instead"); + } + + #[test] + fn toggle_breakpoint_intercept_fails_if_monitor_write_ctrlreg_fails() { + let mut xencontrol_mock = MockXenControl::default(); + let mut xenevtchn_mock = MockXenEventChannel::default(); + let mut xenforeignmemory_mock = MockXenForeignMem::default(); + let mut xenstore_mock = MockXs::default(); + setup( + &mut xencontrol_mock, + &mut xenevtchn_mock, + &mut xenforeignmemory_mock, + &mut xenstore_mock, + ); + xencontrol_mock + .expect_monitor_software_breakpoint() + .returning(|_, _| Err(xenctrl::error::XcError::new("some error"))); + let mut xen = Xen::new( + "some_vm", + xencontrol_mock, + xenevtchn_mock, + xenforeignmemory_mock, + xenstore_mock, + None, + ) + .expect("Failed to create driver"); + let vcpu = 0; + + let result = xen.toggle_intercept(vcpu, InterceptType::Breakpoint, true); + + assert!(result.is_err(), "Expected error, got ok instead"); + } + + mock! { + XenControl{} + trait Debug { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; + } + trait XenIntrospectable: Debug { + fn init<'a> ( + &mut self, + logger: Option<&'a mut xentoollog_logger>, + dombuild_logger: Option<&'a mut xentoollog_logger>, + open_flags: u32, + ) -> Result<(), xenctrl::error::XcError>; + fn domain_hvm_getcontext_partial(&self, domid: u32, vcpu: u16) -> Result; + fn domain_hvm_setcontext(&self, domid: u32, buffer: *mut c_uint, size: usize) -> Result<(), xenctrl::error::XcError>; + fn domain_hvm_getcontext( + &self, + domid: u32, + vcpu: u16, + ) -> Result<(*mut c_uint, hvm_hw_cpu, u32), xenctrl::error::XcError>; + fn monitor_enable(&mut self, domid: u32) -> Result<(vm_event_sring, vm_event_back_ring, u32), xenctrl::error::XcError>; + fn get_request(&self, back_ring: &mut vm_event_back_ring) -> Result; + fn put_response( + &self, + rsp: &mut vm_event_response_t, + back_ring: &mut vm_event_back_ring, + ) -> Result<(), xenctrl::error::XcError>; + fn get_event_type(&self, req: vm_event_request_t) -> Result; + fn monitor_disable(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn domain_pause(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn domain_unpause(&self, domid: u32) -> Result<(), xenctrl::error::XcError>; + fn monitor_software_breakpoint(&self, domid: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_singlestep(&self, domid: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_mov_to_msr(&self, domid: u32, msr: u32, enable: bool) -> Result<(), xenctrl::error::XcError>; + fn monitor_write_ctrlreg( + &self, + domid: u32, + index: XenCr, + enable: bool, + sync: bool, + onchangeonly: bool, + ) -> Result<(), xenctrl::error::XcError>; + fn set_mem_access( + &self, + domid: u32, + access: XenPageAccess, + first_pfn: u64, + ) -> Result<(), xenctrl::error::XcError>; + fn get_mem_access(&self, domid: u32, pfn: u64) -> Result; + fn domain_maximum_gpfn(&self, domid: u32) -> Result; + fn close(&mut self) -> Result<(), xenctrl::error::XcError>; + } + } + + mock! { XenEventChannel{} trait Debug { fn fmt<'a>(&self, f: &mut Formatter<'a>) -> std::fmt::Result; @@ -585,7 +1786,7 @@ mod tests { } trait XenForeignMemoryIntrospectable: Debug { fn init(&mut self) -> Result<(), failure::Error>; - fn map(&self, domid: u32, prot: c_int, gfn: u64) -> Result<&mut [u8], failure::Error>; + fn map<'a> (&'a self, domid: u32, prot: c_int, gfn: u64) -> Result<&'a mut [u8], failure::Error>; fn unmap(&self, page: &mut [u8]) -> Result<(), Box>; fn close(&mut self) -> Result<(), failure::Error>; } diff --git a/src/lib.rs b/src/lib.rs index 87acefd6..1850ef24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,6 @@ extern crate bitflags; #[macro_use] extern crate failure; - use api::Introspectable; use api::{DriverInitParam, DriverType}; use driver::dummy::Dummy;