@@ -122,7 +122,9 @@ impl GuestMemory {
122
122
false
123
123
}
124
124
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.
126
128
pub fn checked_offset ( & self , base : GuestAddress , offset : usize ) -> Option < GuestAddress > {
127
129
if let Some ( addr) = base. checked_add ( offset) {
128
130
for region in self . regions . iter ( ) {
@@ -134,6 +136,25 @@ impl GuestMemory {
134
136
None
135
137
}
136
138
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
+
137
158
/// Returns the size of the memory region.
138
159
pub fn num_regions ( & self ) -> usize {
139
160
self . regions . len ( )
@@ -366,10 +387,14 @@ impl GuestMemory {
366
387
/// Converts a GuestAddress into a pointer in the address space of this
367
388
/// process. This should only be necessary for giving addresses to the
368
389
/// 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.
370
394
///
371
395
/// # Arguments
372
396
/// * `guest_addr` - Guest address to convert.
397
+ /// * `size` - The size of the range to validate starting at `guest_addr`.
373
398
///
374
399
/// # Examples
375
400
///
@@ -378,13 +403,13 @@ impl GuestMemory {
378
403
/// # fn test_host_addr() -> Result<(), ()> {
379
404
/// let start_addr = GuestAddress(0x1000);
380
405
/// 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();
382
407
/// println!("Host address is {:p}", addr);
383
408
/// Ok(())
384
409
/// # }
385
410
/// ```
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| {
388
413
// This is safe; `do_in_region` already checks that offset is in
389
414
// bounds.
390
415
Ok ( unsafe { mapping. as_ptr ( ) . add ( offset) } as * const u8 )
@@ -486,9 +511,48 @@ mod tests {
486
511
let end_addr = GuestAddress ( 0xc00 ) ;
487
512
assert ! ( !guest_mem. address_in_range( end_addr) ) ;
488
513
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.
490
533
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.
491
537
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( ) ) ;
492
556
}
493
557
494
558
#[ test]
@@ -614,17 +678,41 @@ mod tests {
614
678
let start_addr2 = GuestAddress ( 0x100 ) ;
615
679
let mem = GuestMemory :: new ( & [ ( start_addr1, 0x100 ) , ( start_addr2, 0x400 ) ] ) . unwrap ( ) ;
616
680
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
+
617
705
// Verify the host addresses match what we expect from the mappings.
618
706
let addr1_base = get_mapping ( & mem, start_addr1) . unwrap ( ) ;
619
707
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 ( ) ;
622
710
assert_eq ! ( host_addr1, addr1_base) ;
623
711
assert_eq ! ( host_addr2, addr2_base) ;
624
712
625
713
// Check that a bad address returns an error.
626
714
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( ) ) ;
628
716
}
629
717
630
718
#[ test]
0 commit comments