Snap for 9470583 from d3a0a4237549fbdc7211871c0280e7858ff013fc to tm-qpr3-release

Change-Id: I3cf3372fd54e3c0e7b6a455f5b65454a39421c3c
diff --git a/devices/src/virtio/queue.rs b/devices/src/virtio/queue.rs
index a436f74..1e4d8bd 100644
--- a/devices/src/virtio/queue.rs
+++ b/devices/src/virtio/queue.rs
@@ -162,11 +162,8 @@
         if self.len > 0 {
             match self.get_mem_regions() {
                 Ok(regions) => {
-                    if regions.iter().any(|r| {
-                        self.mem
-                            .checked_offset(r.gpa, r.len as u64 - 1u64)
-                            .is_none()
-                    }) {
+                    // Each region in `self.regions` must be a contiguous range in `self.mem`.
+                    if !regions.iter().all(|r| self.mem.is_valid_range(r.gpa, r.len as u64)) {
                         return false;
                     }
                 }
diff --git a/vm_memory/src/guest_memory.rs b/vm_memory/src/guest_memory.rs
index 47c3ba4..7c5b4d4 100644
--- a/vm_memory/src/guest_memory.rs
+++ b/vm_memory/src/guest_memory.rs
@@ -315,7 +315,10 @@
             .any(|region| region.start() < end && start < region.end())
     }
 
-    /// Returns the address plus the offset if it is in range.
+    /// Returns an address `addr + offset` if it's in range.
+    ///
+    /// This function doesn't care whether a region `[addr, addr + offset)` is in range or not. To
+    /// guarantee it's a valid range, use `is_valid_range()` instead.
     pub fn checked_offset(&self, addr: GuestAddress, offset: u64) -> Option<GuestAddress> {
         addr.checked_add(offset).and_then(|a| {
             if self.address_in_range(a) {
@@ -326,6 +329,24 @@
         })
     }
 
+    /// Returns true if the given range `[start, start + length)` is a valid contiguous memory
+    /// range available to the guest and it's backed by a single underlying memory region.
+    pub fn is_valid_range(&self, start: GuestAddress, length: u64) -> bool {
+        if length == 0 {
+            return false;
+        }
+
+        let end = if let Some(end) = start.checked_add(length - 1) {
+            end
+        } else {
+            return false;
+        };
+
+        self.regions
+            .iter()
+            .any(|region| region.start() <= start && end < region.end())
+    }
+
     /// Returns the size of the memory region in bytes.
     pub fn num_regions(&self) -> u64 {
         self.regions.len() as u64