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,16 +321,16 @@ 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
} ;
309
331
310
332
vfio_syscall:: unmap_dma ( self , & mut dma_unmap) ?;
311
- if dma_unmap. size != size {
333
+ if dma_unmap. size != u64 :: try_from ( size) . unwrap ( ) {
312
334
return Err ( VfioError :: InvalidDmaUnmapSize ) ;
313
335
}
314
336
@@ -319,29 +341,68 @@ impl VfioContainer {
319
341
///
320
342
/// # Parameters
321
343
/// * mem: pinned guest memory which could be accessed by devices binding to the container.
322
- pub fn vfio_map_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
344
+ ///
345
+ /// # Safety
346
+ ///
347
+ /// Each of the memory regions must uphold the safety invariants of [`Self::vfio_dma_map`].
348
+ /// Additionally, the [`GuestMemory`] implementation must be well-behaved and uphold the
349
+ /// contracts in its documentation.
350
+ ///
351
+ /// If this function fails (returning [`core::result::Result::Err`]) there is no way to know
352
+ /// which parts of the guest memory were successfully mapped. The only safe action to take
353
+ /// to avoid undefined guest behavior is to crash the guest.
354
+ ///
355
+ /// # Errors
356
+ ///
357
+ /// Fails if any of the mapping operations fail.
358
+ ///
359
+ /// # Panics
360
+ ///
361
+ /// Panics if the length of one of the regions overflows `usize`.
362
+ pub unsafe fn vfio_map_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
323
363
mem. iter ( ) . try_for_each ( |region| {
324
364
let host_addr = region
325
365
. get_host_address ( MemoryRegionAddress ( 0 ) )
326
366
. map_err ( |_| VfioError :: GetHostAddress ) ?;
327
- self . vfio_dma_map (
328
- region. start_addr ( ) . raw_value ( ) ,
329
- region. len ( ) ,
330
- host_addr as u64 ,
331
- )
367
+ // SAFETY: GuestMemory guarantees the requirements
368
+ // are upheld.
369
+ unsafe {
370
+ self . vfio_dma_map (
371
+ region. start_addr ( ) . raw_value ( ) ,
372
+ region. len ( ) . try_into ( ) . unwrap ( ) ,
373
+ host_addr,
374
+ )
375
+ }
332
376
} )
333
377
}
334
378
335
379
/// Remove all guest memory regions from the vfio container's iommu table.
336
380
///
337
- /// The vfio kernel driver and device hardware couldn't access this guest memory after
338
- /// returning from the function.
381
+ /// The vfio kernel driver and device hardware can't access this guest memory after
382
+ /// the function returns successfully, **provided that the following precondition holds**.
383
+ ///
384
+ /// # Precondition
385
+ ///
386
+ /// A previous call to [`Self::vfio_map_guest_memory`] must have succeeded, and iterating
387
+ /// over the regions in the [`GuestMemory`] must produce the same values it did in the
388
+ /// past. This latter contract will be upheld by a correct [`GuestMemory`] implementation,
389
+ /// but [`GuestMemory`] is a safe trait and so unsafe code must not rely on this unless it
390
+ /// is explicitly part of a safety contract.
339
391
///
340
392
/// # Parameters
341
393
/// * mem: pinned guest memory which could be accessed by devices binding to the container.
394
+ ///
395
+ /// # Panics
396
+ ///
397
+ /// Panics if the length of any of the regions overflows `usize`. That should have been
398
+ /// caught by [`Self::vfio_map_guest_memory`], so it indicates a bogus [`GuestMemory`]
399
+ /// implementation.
342
400
pub fn vfio_unmap_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
343
401
mem. iter ( ) . try_for_each ( |region| {
344
- self . vfio_dma_unmap ( region. start_addr ( ) . raw_value ( ) , region. len ( ) )
402
+ self . vfio_dma_unmap (
403
+ region. start_addr ( ) . raw_value ( ) ,
404
+ region. len ( ) . try_into ( ) . unwrap ( ) ,
405
+ )
345
406
} )
346
407
}
347
408
@@ -1358,8 +1419,10 @@ mod tests {
1358
1419
container. put_group ( group3) ;
1359
1420
assert_eq ! ( Arc :: strong_count( & group) , 1 ) ;
1360
1421
1361
- container. vfio_dma_map ( 0x1000 , 0x1000 , 0x8000 ) . unwrap ( ) ;
1362
- container. vfio_dma_map ( 0x2000 , 0x2000 , 0x8000 ) . unwrap_err ( ) ;
1422
+ // SAFETY: this is a test implementation that does not access memory
1423
+ unsafe { container. vfio_dma_map ( 0x1000 , 0x1000 , 0x8000 as _ ) } . unwrap ( ) ;
1424
+ // SAFETY: this is a test implementation that does not access memory
1425
+ unsafe { container. vfio_dma_map ( 0x2000 , 0x2000 , 0x8000 as _ ) } . unwrap_err ( ) ;
1363
1426
container. vfio_dma_unmap ( 0x1000 , 0x1000 ) . unwrap ( ) ;
1364
1427
container. vfio_dma_unmap ( 0x2000 , 0x2000 ) . unwrap_err ( ) ;
1365
1428
}
@@ -1506,7 +1569,9 @@ mod tests {
1506
1569
let mem1 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr1, 0x1000 ) ] ) . unwrap ( ) ;
1507
1570
let container = create_vfio_container ( ) ;
1508
1571
1509
- container. vfio_map_guest_memory ( & mem1) . unwrap ( ) ;
1572
+ // SAFETY: This is a dummy implementation that does not access
1573
+ // memory.
1574
+ unsafe { container. vfio_map_guest_memory ( & mem1) } . unwrap ( ) ;
1510
1575
1511
1576
let addr2 = GuestAddress ( 0x3000 ) ;
1512
1577
let mem2 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr2, 0x1000 ) ] ) . unwrap ( ) ;
0 commit comments