diff options
author | Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com> | 2019-10-31 13:16:02 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-11-11 09:46:43 +0000 |
commit | d9a54c222ea0e83fc4f237f792b374b2841074c5 (patch) | |
tree | 5939006b31f80e045be2d6c05982d7c216c9426e | |
parent | 3f8599aea9d06af32e3368d45e19edc2666453a8 (diff) | |
download | crosvm-d9a54c222ea0e83fc4f237f792b374b2841074c5.tar crosvm-d9a54c222ea0e83fc4f237f792b374b2841074c5.tar.gz crosvm-d9a54c222ea0e83fc4f237f792b374b2841074c5.tar.bz2 crosvm-d9a54c222ea0e83fc4f237f792b374b2841074c5.tar.lz crosvm-d9a54c222ea0e83fc4f237f792b374b2841074c5.tar.xz crosvm-d9a54c222ea0e83fc4f237f792b374b2841074c5.tar.zst crosvm-d9a54c222ea0e83fc4f237f792b374b2841074c5.zip |
kvm: Allow low mmio added into kvm
gpa > guest_mem.end_addr() is used to avoid gpa fall into guest ram, but low mmio maybe below guest_mem.end_addr(), this condition is false, then low mmio couldn't be added. Since low mmio could be added into kvm also, this condition is wrong. This patch iterate all the guest memory reginos, and check whether it overlap with any of them. BUG=chromium:992270 TEST=bulld_test Change-Id: I9560db43f9836f85d0ff927e7eeb92447774568c Signed-off-by: Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1895235 Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
-rw-r--r-- | kvm/src/lib.rs | 23 | ||||
-rw-r--r-- | sys_util/src/guest_memory.rs | 21 |
2 files changed, 38 insertions, 6 deletions
diff --git a/kvm/src/lib.rs b/kvm/src/lib.rs index 8d3c61f..0a8d5c1 100644 --- a/kvm/src/lib.rs +++ b/kvm/src/lib.rs @@ -16,7 +16,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::ptr::copy_nonoverlapping; use libc::sigset_t; -use libc::{open, EINVAL, ENOENT, ENOSPC, O_CLOEXEC, O_RDWR}; +use libc::{open, EINVAL, ENOENT, ENOSPC, EOVERFLOW, O_CLOEXEC, O_RDWR}; use kvm_sys::*; @@ -424,7 +424,9 @@ impl Vm { read_only: bool, log_dirty_pages: bool, ) -> Result<u32> { - if guest_addr < self.guest_mem.end_addr() { + let size = mem.size() as u64; + let end_addr = guest_addr.checked_add(size).ok_or(Error::new(EOVERFLOW))?; + if self.guest_mem.range_overlap(guest_addr, end_addr) { return Err(Error::new(ENOSPC)); } @@ -437,7 +439,7 @@ impl Vm { read_only, log_dirty_pages, guest_addr.offset() as u64, - mem.size() as u64, + size, mem.as_ptr(), )? }; @@ -483,7 +485,9 @@ impl Vm { read_only: bool, log_dirty_pages: bool, ) -> Result<u32> { - if guest_addr < self.guest_mem.end_addr() { + let size = mmap_arena.size() as u64; + let end_addr = guest_addr.checked_add(size).ok_or(Error::new(EOVERFLOW))?; + if self.guest_mem.range_overlap(guest_addr, end_addr) { return Err(Error::new(ENOSPC)); } @@ -496,7 +500,7 @@ impl Vm { read_only, log_dirty_pages, guest_addr.offset() as u64, - mmap_arena.size() as u64, + size, mmap_arena.as_ptr(), )? }; @@ -1923,12 +1927,19 @@ mod tests { #[test] fn add_memory() { let kvm = Kvm::new().unwrap(); - let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap(); + let gm = GuestMemory::new(&vec![ + (GuestAddress(0), 0x1000), + (GuestAddress(0x5000), 0x5000), + ]) + .unwrap(); let mut vm = Vm::new(&kvm, gm).unwrap(); let mem_size = 0x1000; let mem = MemoryMapping::new(mem_size).unwrap(); vm.add_mmio_memory(GuestAddress(0x1000), mem, false, false) .unwrap(); + let mem = MemoryMapping::new(mem_size).unwrap(); + vm.add_mmio_memory(GuestAddress(0x10000), mem, false, false) + .unwrap(); } #[test] diff --git a/sys_util/src/guest_memory.rs b/sys_util/src/guest_memory.rs index d40b5be..162960f 100644 --- a/sys_util/src/guest_memory.rs +++ b/sys_util/src/guest_memory.rs @@ -229,6 +229,14 @@ impl GuestMemory { .any(|region| region.guest_base <= addr && addr < region_end(region)) } + /// Returns true if the given range (start, end) is overlap with the memory range + /// available to the guest. + pub fn range_overlap(&self, start: GuestAddress, end: GuestAddress) -> bool { + self.regions + .iter() + .any(|region| region.guest_base < end && start < region_end(region)) + } + /// Returns the address plus the offset if it is in range. pub fn checked_offset(&self, addr: GuestAddress, offset: u64) -> Option<GuestAddress> { addr.checked_add(offset).and_then(|a| { @@ -614,6 +622,19 @@ mod tests { assert_eq!(gm.address_in_range(GuestAddress(0x3000)), false); assert_eq!(gm.address_in_range(GuestAddress(0x5000)), true); assert_eq!(gm.address_in_range(GuestAddress(0x6000)), false); + assert_eq!(gm.address_in_range(GuestAddress(0x6000)), false); + assert_eq!( + gm.range_overlap(GuestAddress(0x1000), GuestAddress(0x3000)), + true + ); + assert_eq!( + gm.range_overlap(GuestAddress(0x3000), GuestAddress(0x4000)), + false + ); + assert_eq!( + gm.range_overlap(GuestAddress(0x3000), GuestAddress(0x7000)), + true + ); assert!(gm.checked_offset(GuestAddress(0x1000), 0x1000).is_none()); assert!(gm.checked_offset(GuestAddress(0x5000), 0x800).is_some()); assert!(gm.checked_offset(GuestAddress(0x5000), 0x1000).is_none()); |