summary refs log tree commit diff
diff options
context:
space:
mode:
authorXiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>2019-10-31 13:16:02 +0800
committerCommit Bot <commit-bot@chromium.org>2019-11-11 09:46:43 +0000
commitd9a54c222ea0e83fc4f237f792b374b2841074c5 (patch)
tree5939006b31f80e045be2d6c05982d7c216c9426e
parent3f8599aea9d06af32e3368d45e19edc2666453a8 (diff)
downloadcrosvm-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.rs23
-rw-r--r--sys_util/src/guest_memory.rs21
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());