4
4
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
5
5
6
6
use std:: collections:: HashMap ;
7
+ use std:: convert:: { TryFrom as _, TryInto as _} ;
7
8
use std:: ffi:: CString ;
8
9
use std:: fs:: { File , OpenOptions } ;
9
10
use std:: mem:: { self , ManuallyDrop } ;
@@ -279,16 +280,37 @@ impl VfioContainer {
279
280
/// Map a region of guest memory regions into the vfio container's iommu table.
280
281
///
281
282
/// # Parameters
283
+ ///
282
284
/// * iova: IO virtual address to mapping the memory.
283
285
/// * size: size of the memory region.
284
286
/// * user_addr: host virtual address for the guest memory region to map.
285
- pub fn vfio_dma_map ( & self , iova : u64 , size : u64 , user_addr : u64 ) -> Result < ( ) > {
287
+ ///
288
+ /// # Safety
289
+ ///
290
+ /// Until [`Self::vfio_dma_unmap`] is successfully called with identical
291
+ /// values for iova and size, or until the entire range of `[user_addr..user_addr+size)`
292
+ /// has been unmapped with successful calls to `munmap` or replaced with successful calls
293
+ /// to `mmap(MAP_FIXED)`, the only safe uses of the address range
294
+ /// `[user_addr..user_addr+size)` are:
295
+ ///
296
+ /// - Atomic and/or volatile operations on raw pointers.
297
+ /// - Sharing the underlying storage with another process or a guest VM.
298
+ /// - Passing a pointer to the memory to a system call (such as `read()`
299
+ /// or `write()`) that is safe regardless of the memory's contents.
300
+ /// - Passing a raw pointer to functions that only do one of the above things.
301
+ ///
302
+ /// In particular, creating a Rust reference to this memory is instant undefined behavior
303
+ /// due to the Rust aliasing rules. It is also undefined behavior to call this function if
304
+ /// a Rust reference to this memory is live.
305
+ pub unsafe fn vfio_dma_map ( & self , iova : u64 , size : usize , user_addr : * mut u8 ) -> Result < ( ) > {
306
+ const _: ( ) = assert ! ( mem:: size_of:: <u64 >( ) >= mem:: size_of:: <* mut u8 >( ) ) ;
307
+ const _: ( ) = assert ! ( mem:: size_of:: <u64 >( ) >= mem:: size_of:: <usize >( ) ) ;
286
308
let dma_map = vfio_iommu_type1_dma_map {
287
309
argsz : mem:: size_of :: < vfio_iommu_type1_dma_map > ( ) as u32 ,
288
310
flags : VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE ,
289
- vaddr : user_addr,
311
+ vaddr : ( user_addr as usize ) . try_into ( ) . unwrap ( ) ,
290
312
iova,
291
- size,
313
+ size : size . try_into ( ) . unwrap ( ) ,
292
314
} ;
293
315
294
316
vfio_syscall:: map_dma ( self , & dma_map)
@@ -299,17 +321,17 @@ impl VfioContainer {
299
321
/// # Parameters
300
322
/// * iova: IO virtual address to mapping the memory.
301
323
/// * size: size of the memory region.
302
- pub fn vfio_dma_unmap ( & self , iova : u64 , size : u64 ) -> Result < ( ) > {
324
+ pub fn vfio_dma_unmap ( & self , iova : u64 , size : usize ) -> Result < ( ) > {
303
325
let mut dma_unmap = vfio_iommu_type1_dma_unmap {
304
326
argsz : mem:: size_of :: < vfio_iommu_type1_dma_unmap > ( ) as u32 ,
305
327
flags : 0 ,
306
328
iova,
307
- size,
329
+ size : size . try_into ( ) . unwrap ( ) ,
308
330
..Default :: default ( )
309
331
} ;
310
332
311
333
vfio_syscall:: unmap_dma ( self , & mut dma_unmap) ?;
312
- if dma_unmap. size != size {
334
+ if dma_unmap. size != u64 :: try_from ( size) . unwrap ( ) {
313
335
return Err ( VfioError :: InvalidDmaUnmapSize ) ;
314
336
}
315
337
@@ -320,29 +342,68 @@ impl VfioContainer {
320
342
///
321
343
/// # Parameters
322
344
/// * mem: pinned guest memory which could be accessed by devices binding to the container.
323
- pub fn vfio_map_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
345
+ ///
346
+ /// # Safety
347
+ ///
348
+ /// Each of the memory regions must uphold the safety invariants of [`Self::vfio_dma_map`].
349
+ /// Additionally, the [`GuestMemory`] implementation must be well-behaved and uphold the
350
+ /// contracts in its documentation.
351
+ ///
352
+ /// If this function fails (returning [`core::result::Result::Err`]) there is no way to know
353
+ /// which parts of the guest memory were successfully mapped. The only safe action to take
354
+ /// to avoid undefined guest behavior is to crash the guest.
355
+ ///
356
+ /// # Errors
357
+ ///
358
+ /// Fails if any of the mapping operations fail.
359
+ ///
360
+ /// # Panics
361
+ ///
362
+ /// Panics if the length of one of the regions overflows `usize`.
363
+ pub unsafe fn vfio_map_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
324
364
mem. iter ( ) . try_for_each ( |region| {
325
365
let host_addr = region
326
366
. get_host_address ( MemoryRegionAddress ( 0 ) )
327
367
. map_err ( |_| VfioError :: GetHostAddress ) ?;
328
- self . vfio_dma_map (
329
- region. start_addr ( ) . raw_value ( ) ,
330
- region. len ( ) ,
331
- host_addr as u64 ,
332
- )
368
+ // SAFETY: GuestMemory guarantees the requirements
369
+ // are upheld.
370
+ unsafe {
371
+ self . vfio_dma_map (
372
+ region. start_addr ( ) . raw_value ( ) ,
373
+ region. len ( ) . try_into ( ) . unwrap ( ) ,
374
+ host_addr,
375
+ )
376
+ }
333
377
} )
334
378
}
335
379
336
380
/// Remove all guest memory regions from the vfio container's iommu table.
337
381
///
338
- /// The vfio kernel driver and device hardware couldn't access this guest memory after
339
- /// returning from the function.
382
+ /// The vfio kernel driver and device hardware can't access this guest memory after
383
+ /// the function returns successfully, **provided that the following precondition holds**.
384
+ ///
385
+ /// # Precondition
386
+ ///
387
+ /// A previous call to [`Self::vfio_map_guest_memory`] must have succeeded, and iterating
388
+ /// over the regions in the [`GuestMemory`] must produce the same values it did in the
389
+ /// past. This latter contract will be upheld by a correct [`GuestMemory`] implementation,
390
+ /// but [`GuestMemory`] is a safe trait and so unsafe code must not rely on this unless it
391
+ /// is explicitly part of a safety contract.
340
392
///
341
393
/// # Parameters
342
394
/// * mem: pinned guest memory which could be accessed by devices binding to the container.
395
+ ///
396
+ /// # Panics
397
+ ///
398
+ /// Panics if the length of any of the regions overflows `usize`. That should have been
399
+ /// caught by [`Self::vfio_map_guest_memory`], so it indicates a bogus [`GuestMemory`]
400
+ /// implementation.
343
401
pub fn vfio_unmap_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
344
402
mem. iter ( ) . try_for_each ( |region| {
345
- self . vfio_dma_unmap ( region. start_addr ( ) . raw_value ( ) , region. len ( ) )
403
+ self . vfio_dma_unmap (
404
+ region. start_addr ( ) . raw_value ( ) ,
405
+ region. len ( ) . try_into ( ) . unwrap ( ) ,
406
+ )
346
407
} )
347
408
}
348
409
@@ -1361,8 +1422,10 @@ mod tests {
1361
1422
container. put_group ( group3) ;
1362
1423
assert_eq ! ( Arc :: strong_count( & group) , 1 ) ;
1363
1424
1364
- container. vfio_dma_map ( 0x1000 , 0x1000 , 0x8000 ) . unwrap ( ) ;
1365
- container. vfio_dma_map ( 0x2000 , 0x2000 , 0x8000 ) . unwrap_err ( ) ;
1425
+ // SAFETY: this is a test implementation that does not access memory
1426
+ unsafe { container. vfio_dma_map ( 0x1000 , 0x1000 , 0x8000 as _ ) } . unwrap ( ) ;
1427
+ // SAFETY: this is a test implementation that does not access memory
1428
+ unsafe { container. vfio_dma_map ( 0x2000 , 0x2000 , 0x8000 as _ ) } . unwrap_err ( ) ;
1366
1429
container. vfio_dma_unmap ( 0x1000 , 0x1000 ) . unwrap ( ) ;
1367
1430
container. vfio_dma_unmap ( 0x2000 , 0x2000 ) . unwrap_err ( ) ;
1368
1431
}
@@ -1509,7 +1572,9 @@ mod tests {
1509
1572
let mem1 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr1, 0x1000 ) ] ) . unwrap ( ) ;
1510
1573
let container = create_vfio_container ( ) ;
1511
1574
1512
- container. vfio_map_guest_memory ( & mem1) . unwrap ( ) ;
1575
+ // SAFETY: This is a dummy implementation that does not access
1576
+ // memory.
1577
+ unsafe { container. vfio_map_guest_memory ( & mem1) } . unwrap ( ) ;
1513
1578
1514
1579
let addr2 = GuestAddress ( 0x3000 ) ;
1515
1580
let mem2 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr2, 0x1000 ) ] ) . unwrap ( ) ;
0 commit comments