Skip to content

Commit d9d2ec1

Browse files
jiangliuandreeaflorescu
authored andcommitted
memory_model: validate memory region before copying memory content
Split the memory copy operations into to cases: 1) Full copy by do_in_region() which must copy all requested memory content or fails. Pass an additional parameter 'size' into function do_in_region(), and validate memory region before copying memory content. By this way the destination memory will be either fully copied or untouched. 2) Partial copy by do_in_region_patial() which copies memory as much as possible and returns size of copied memory content. Signed-off-by: Liu Jiang <[email protected]>
1 parent da21ac0 commit d9d2ec1

File tree

1 file changed

+57
-12
lines changed

1 file changed

+57
-12
lines changed

memory_model/src/guest_memory.rs

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
//! Track memory regions that are mapped to the guest microVM.
99
1010
use std::io::{Read, Write};
11-
use std::result;
1211
use std::sync::Arc;
12+
use std::{mem, result};
1313

1414
use guest_address::GuestAddress;
1515
use mmap::{self, MemoryMapping};
@@ -20,6 +20,8 @@ use DataInit;
2020
pub enum Error {
2121
/// Failure in finding a guest address in any memory regions mapped by this guest.
2222
InvalidGuestAddress(GuestAddress),
23+
/// Failure in finding a guest address range in any memory regions mapped by this guest.
24+
InvalidGuestAddressRange(GuestAddress, usize),
2325
/// Failure in accessing the memory located at some address.
2426
MemoryAccess(GuestAddress, mmap::Error),
2527
/// Failure in creating an anonymous shared mapping.
@@ -156,6 +158,7 @@ impl GuestMemory {
156158
}
157159
Ok(())
158160
}
161+
159162
/// Writes a slice to guest memory at the specified guest address.
160163
/// Returns the number of bytes written. The number of bytes written can
161164
/// be less than the length of the slice if there isn't enough room in the
@@ -175,7 +178,7 @@ impl GuestMemory {
175178
/// # }
176179
/// ```
177180
pub fn write_slice_at_addr(&self, buf: &[u8], guest_addr: GuestAddress) -> Result<usize> {
178-
self.do_in_region(guest_addr, move |mapping, offset| {
181+
self.do_in_region_partial(guest_addr, move |mapping, offset| {
179182
mapping
180183
.write_slice(buf, offset)
181184
.map_err(|e| Error::MemoryAccess(guest_addr, e))
@@ -206,7 +209,7 @@ impl GuestMemory {
206209
mut buf: &mut [u8],
207210
guest_addr: GuestAddress,
208211
) -> Result<usize> {
209-
self.do_in_region(guest_addr, move |mapping, offset| {
212+
self.do_in_region_partial(guest_addr, move |mapping, offset| {
210213
mapping
211214
.read_slice(buf, offset)
212215
.map_err(|e| Error::MemoryAccess(guest_addr, e))
@@ -218,6 +221,9 @@ impl GuestMemory {
218221
/// mid-read. However, as long as the type T is plain old data and can
219222
/// handle random initialization, everything will be OK.
220223
///
224+
/// Caller needs to guarantee that the object does not cross MemoryRegion
225+
/// boundary, otherwise it fails.
226+
///
221227
/// # Examples
222228
/// * Read a u64 from two areas of guest memory backed by separate mappings.
223229
///
@@ -234,7 +240,7 @@ impl GuestMemory {
234240
/// # }
235241
/// ```
236242
pub fn read_obj_from_addr<T: DataInit>(&self, guest_addr: GuestAddress) -> Result<T> {
237-
self.do_in_region(guest_addr, |mapping, offset| {
243+
self.do_in_region(guest_addr, mem::size_of::<T>(), |mapping, offset| {
238244
mapping
239245
.read_obj(offset)
240246
.map_err(|e| Error::MemoryAccess(guest_addr, e))
@@ -244,6 +250,9 @@ impl GuestMemory {
244250
/// Writes an object to the memory region at the specified guest address.
245251
/// Returns Ok(()) if the object fits, or Err if it extends past the end.
246252
///
253+
/// Caller needs to guarantee that the object does not cross MemoryRegion
254+
/// boundary, otherwise it fails.
255+
///
247256
/// # Examples
248257
/// * Write a u64 at guest address 0x1100.
249258
///
@@ -257,7 +266,7 @@ impl GuestMemory {
257266
/// # }
258267
/// ```
259268
pub fn write_obj_at_addr<T: DataInit>(&self, val: T, guest_addr: GuestAddress) -> Result<()> {
260-
self.do_in_region(guest_addr, move |mapping, offset| {
269+
self.do_in_region(guest_addr, mem::size_of::<T>(), move |mapping, offset| {
261270
mapping
262271
.write_obj(val, offset)
263272
.map_err(|e| Error::MemoryAccess(guest_addr, e))
@@ -299,7 +308,7 @@ impl GuestMemory {
299308
where
300309
F: Read,
301310
{
302-
self.do_in_region(guest_addr, move |mapping, offset| {
311+
self.do_in_region(guest_addr, count, move |mapping, offset| {
303312
mapping
304313
.read_to_memory(offset, src, count)
305314
.map_err(|e| Error::MemoryAccess(guest_addr, e))
@@ -339,7 +348,7 @@ impl GuestMemory {
339348
where
340349
F: Write,
341350
{
342-
self.do_in_region(guest_addr, move |mapping, offset| {
351+
self.do_in_region(guest_addr, count, move |mapping, offset| {
343352
mapping
344353
.write_from_memory(offset, dst, count)
345354
.map_err(|e| Error::MemoryAccess(guest_addr, e))
@@ -367,7 +376,7 @@ impl GuestMemory {
367376
/// # }
368377
/// ```
369378
pub fn get_host_address(&self, guest_addr: GuestAddress) -> Result<*const u8> {
370-
self.do_in_region(guest_addr, |mapping, offset| {
379+
self.do_in_region(guest_addr, 1, |mapping, offset| {
371380
// This is safe; `do_in_region` already checks that offset is in
372381
// bounds.
373382
Ok(unsafe { mapping.as_ptr().offset(offset as isize) } as *const u8)
@@ -413,9 +422,27 @@ impl GuestMemory {
413422
self.regions.iter().enumerate().map(mapf).fold(init, foldf)
414423
}
415424

416-
fn do_in_region<F, T>(&self, guest_addr: GuestAddress, cb: F) -> Result<T>
425+
/// Read the whole object from a single MemoryRegion
426+
fn do_in_region<F, T>(&self, guest_addr: GuestAddress, size: usize, cb: F) -> Result<T>
417427
where
418428
F: FnOnce(&MemoryMapping, usize) -> Result<T>,
429+
{
430+
for region in self.regions.iter() {
431+
if guest_addr >= region.guest_base && guest_addr < region_end(region) {
432+
let offset = guest_addr.offset_from(region.guest_base);
433+
if size <= region.mapping.size() - offset {
434+
return cb(&region.mapping, offset);
435+
}
436+
break;
437+
}
438+
}
439+
Err(Error::InvalidGuestAddressRange(guest_addr, size))
440+
}
441+
442+
/// Read the whole or partial content from a single MemoryRegion
443+
fn do_in_region_partial<F>(&self, guest_addr: GuestAddress, cb: F) -> Result<usize>
444+
where
445+
F: FnOnce(&MemoryMapping, usize) -> Result<usize>,
419446
{
420447
for region in self.regions.iter() {
421448
if guest_addr >= region.guest_base && guest_addr < region_end(region) {
@@ -471,14 +498,27 @@ mod tests {
471498
let start_addr1 = GuestAddress(0x0);
472499
let start_addr2 = GuestAddress(0x1000);
473500
let bad_addr = GuestAddress(0x2001);
501+
let bad_addr2 = GuestAddress(0x1ffc);
474502

475503
let gm = GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x1000)]).unwrap();
476504

477505
let val1: u64 = 0xaa55aa55aa55aa55;
478506
let val2: u64 = 0x55aa55aa55aa55aa;
479507
assert_eq!(
480508
format!("{:?}", gm.write_obj_at_addr(val1, bad_addr).err().unwrap()),
481-
format!("InvalidGuestAddress({:?})", bad_addr)
509+
format!(
510+
"InvalidGuestAddressRange({:?}, {:?})",
511+
bad_addr,
512+
std::mem::size_of::<u64>()
513+
)
514+
);
515+
assert_eq!(
516+
format!("{:?}", gm.write_obj_at_addr(val1, bad_addr2).err().unwrap()),
517+
format!(
518+
"InvalidGuestAddressRange({:?}, {:?})",
519+
bad_addr2,
520+
std::mem::size_of::<u64>()
521+
)
482522
);
483523

484524
gm.write_obj_at_addr(val1, GuestAddress(0x500)).unwrap();
@@ -492,7 +532,7 @@ mod tests {
492532

493533
#[test]
494534
fn write_and_read_slice() {
495-
let start_addr = GuestAddress(0x1000);
535+
let mut start_addr = GuestAddress(0x1000);
496536
let gm = GuestMemory::new(&vec![(start_addr, 0x400)]).unwrap();
497537
let sample_buf = &[1, 2, 3, 4, 5];
498538

@@ -501,6 +541,11 @@ mod tests {
501541
let buf = &mut [0u8; 5];
502542
assert_eq!(gm.read_slice_at_addr(buf, start_addr).unwrap(), 5);
503543
assert_eq!(buf, sample_buf);
544+
545+
start_addr = GuestAddress(0x13ff);
546+
assert_eq!(gm.write_slice_at_addr(sample_buf, start_addr).unwrap(), 1);
547+
assert_eq!(gm.read_slice_at_addr(buf, start_addr).unwrap(), 1);
548+
assert_eq!(buf[0], sample_buf[0]);
504549
}
505550

506551
#[test]
@@ -551,7 +596,7 @@ mod tests {
551596

552597
// Get the base address of the mapping for a GuestAddress.
553598
fn get_mapping(mem: &GuestMemory, addr: GuestAddress) -> Result<*const u8> {
554-
mem.do_in_region(addr, |mapping, _| Ok(mapping.as_ptr() as *const u8))
599+
mem.do_in_region(addr, 1, |mapping, _| Ok(mapping.as_ptr() as *const u8))
555600
}
556601

557602
#[test]

0 commit comments

Comments
 (0)