@@ -122,7 +122,9 @@ impl GuestMemory {
122122 false
123123 }
124124
125- /// Returns the address plus the offset if it is in range.
125+ /// Returns the address plus the offset if the result falls within a valid memory region. The
126+ /// resulting address and base address may belong to different memory regions, and the base
127+ /// might not even exist in a valid region.
126128 pub fn checked_offset ( & self , base : GuestAddress , offset : usize ) -> Option < GuestAddress > {
127129 if let Some ( addr) = base. checked_add ( offset) {
128130 for region in self . regions . iter ( ) {
@@ -134,6 +136,25 @@ impl GuestMemory {
134136 None
135137 }
136138
139+ /// Returns the address plus the offset if base and the result belong to the same memory
140+ /// region (Firecracker currently does not use adjacent memory regions, so distinct regions
141+ /// always have a gap in-between).
142+ pub fn checked_range_offset ( & self , base : GuestAddress , offset : usize ) -> Option < GuestAddress > {
143+ if let Some ( addr) = base. checked_add ( offset) {
144+ for region in self . regions . iter ( ) {
145+ let region_end = region_end ( region) ;
146+ if base >= region. guest_base
147+ && base < region_end
148+ && addr >= region. guest_base
149+ && addr < region_end
150+ {
151+ return Some ( addr) ;
152+ }
153+ }
154+ }
155+ None
156+ }
157+
137158 /// Returns the size of the memory region.
138159 pub fn num_regions ( & self ) -> usize {
139160 self . regions . len ( )
@@ -366,10 +387,14 @@ impl GuestMemory {
366387 /// Converts a GuestAddress into a pointer in the address space of this
367388 /// process. This should only be necessary for giving addresses to the
368389 /// kernel, as with vhost ioctls. Normal reads/writes to guest memory should
369- /// be done through `write_from_memory`, `read_obj_from_addr`, etc.
390+ /// be done through `write_from_memory`, `read_obj_from_addr`, etc. This method
391+ /// also checks whether the provided GuestAddress and size define a valid range
392+ /// in the guest memory region, which is helpful to ensure the operation that
393+ /// uses the result does not access memory outside the guest memory mappings.
370394 ///
371395 /// # Arguments
372396 /// * `guest_addr` - Guest address to convert.
397+ /// * `size` - The size of the range to validate starting at `guest_addr`.
373398 ///
374399 /// # Examples
375400 ///
@@ -378,13 +403,13 @@ impl GuestMemory {
378403 /// # fn test_host_addr() -> Result<(), ()> {
379404 /// let start_addr = GuestAddress(0x1000);
380405 /// let mut gm = GuestMemory::new(&vec![(start_addr, 0x500)]).map_err(|_| ())?;
381- /// let addr = gm.get_host_address(GuestAddress(0x1200)).unwrap();
406+ /// let addr = gm.get_host_address(GuestAddress(0x1200), 1 ).unwrap();
382407 /// println!("Host address is {:p}", addr);
383408 /// Ok(())
384409 /// # }
385410 /// ```
386- pub fn get_host_address ( & self , guest_addr : GuestAddress ) -> Result < * const u8 > {
387- self . do_in_region ( guest_addr, 1 , |mapping, offset| {
411+ pub fn get_host_address ( & self , guest_addr : GuestAddress , size : usize ) -> Result < * const u8 > {
412+ self . do_in_region ( guest_addr, size , |mapping, offset| {
388413 // This is safe; `do_in_region` already checks that offset is in
389414 // bounds.
390415 Ok ( unsafe { mapping. as_ptr ( ) . add ( offset) } as * const u8 )
@@ -486,9 +511,48 @@ mod tests {
486511 let end_addr = GuestAddress ( 0xc00 ) ;
487512 assert ! ( !guest_mem. address_in_range( end_addr) ) ;
488513 assert_eq ! ( guest_mem. end_addr( ) , end_addr) ;
489- assert ! ( guest_mem. checked_offset( start_addr1, 0x900 ) . is_some( ) ) ;
514+
515+ // Begins and ends within first region.
516+ assert_eq ! (
517+ guest_mem. checked_offset( start_addr1, 0x300 ) ,
518+ Some ( GuestAddress ( 0x300 ) )
519+ ) ;
520+ assert_eq ! (
521+ guest_mem. checked_range_offset( start_addr1, 0x300 ) ,
522+ Some ( GuestAddress ( 0x300 ) )
523+ ) ;
524+
525+ // Begins in the first region, and ends in the second, crossing the gap.
526+ assert_eq ! (
527+ guest_mem. checked_offset( start_addr1, 0x900 ) ,
528+ Some ( GuestAddress ( 0x900 ) )
529+ ) ;
530+ assert ! ( guest_mem. checked_range_offset( start_addr1, 0x900 ) . is_none( ) ) ;
531+
532+ // Goes past the end of the first region, into the gap.
490533 assert ! ( guest_mem. checked_offset( start_addr1, 0x700 ) . is_none( ) ) ;
534+ assert ! ( guest_mem. checked_range_offset( start_addr1, 0x700 ) . is_none( ) ) ;
535+
536+ // Starts in the second region, and goes past the end of it.
491537 assert ! ( guest_mem. checked_offset( start_addr2, 0xc00 ) . is_none( ) ) ;
538+ assert ! ( guest_mem. checked_range_offset( start_addr2, 0xc00 ) . is_none( ) ) ;
539+
540+ // Exists entirely within the gap.
541+ assert ! ( guest_mem
542+ . checked_offset( GuestAddress ( 0x500 ) , 0x100 )
543+ . is_none( ) ) ;
544+ assert ! ( guest_mem
545+ . checked_range_offset( GuestAddress ( 0x500 ) , 0x100 )
546+ . is_none( ) ) ;
547+
548+ // Starts inside the gap, crosses into the second region.
549+ assert_eq ! (
550+ guest_mem. checked_offset( GuestAddress ( 0x500 ) , 0x400 ) ,
551+ Some ( GuestAddress ( 0x900 ) )
552+ ) ;
553+ assert ! ( guest_mem
554+ . checked_range_offset( GuestAddress ( 0x500 ) , 0x400 )
555+ . is_none( ) ) ;
492556 }
493557
494558 #[ test]
@@ -614,17 +678,41 @@ mod tests {
614678 let start_addr2 = GuestAddress ( 0x100 ) ;
615679 let mem = GuestMemory :: new ( & [ ( start_addr1, 0x100 ) , ( start_addr2, 0x400 ) ] ) . unwrap ( ) ;
616680
681+ assert ! ( mem. get_host_address( start_addr1, 0x100 ) . is_ok( ) ) ;
682+ // Error because we go past the end of the first region.
683+ assert ! ( mem. get_host_address( start_addr1, 0x101 ) . is_err( ) ) ;
684+
685+ assert ! ( mem
686+ . get_host_address( start_addr2. checked_add( 0x100 ) . unwrap( ) , 0x300 )
687+ . is_ok( ) ) ;
688+
689+ // Error because we go past the end of the second region.
690+ assert ! ( mem
691+ . get_host_address( start_addr2. checked_add( 0x100 ) . unwrap( ) , 0x301 )
692+ . is_err( ) ) ;
693+
694+ // Error because we start in the gap between regions.
695+ assert ! ( mem
696+ . get_host_address( start_addr2. checked_sub( 1 ) . unwrap( ) , 0x100 )
697+ . is_err( ) ) ;
698+
699+ // Error because we start in the first region, but when also adding the size we end
700+ // up in the second region.
701+ assert ! ( mem
702+ . get_host_address( start_addr1, start_addr2. 0 + 1 )
703+ . is_err( ) ) ;
704+
617705 // Verify the host addresses match what we expect from the mappings.
618706 let addr1_base = get_mapping ( & mem, start_addr1) . unwrap ( ) ;
619707 let addr2_base = get_mapping ( & mem, start_addr2) . unwrap ( ) ;
620- let host_addr1 = mem. get_host_address ( start_addr1) . unwrap ( ) ;
621- let host_addr2 = mem. get_host_address ( start_addr2) . unwrap ( ) ;
708+ let host_addr1 = mem. get_host_address ( start_addr1, 1 ) . unwrap ( ) ;
709+ let host_addr2 = mem. get_host_address ( start_addr2, 1 ) . unwrap ( ) ;
622710 assert_eq ! ( host_addr1, addr1_base) ;
623711 assert_eq ! ( host_addr2, addr2_base) ;
624712
625713 // Check that a bad address returns an error.
626714 let bad_addr = GuestAddress ( 0x12_3456 ) ;
627- assert ! ( mem. get_host_address( bad_addr) . is_err( ) ) ;
715+ assert ! ( mem. get_host_address( bad_addr, 1 ) . is_err( ) ) ;
628716 }
629717
630718 #[ test]
0 commit comments