44// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
55
66use std:: collections:: HashMap ;
7+ use std:: convert:: { TryFrom as _, TryInto as _} ;
78use std:: ffi:: CString ;
89use std:: fs:: { File , OpenOptions } ;
910use std:: mem:: { self , ManuallyDrop } ;
@@ -279,16 +280,37 @@ impl VfioContainer {
279280 /// Map a region of guest memory regions into the vfio container's iommu table.
280281 ///
281282 /// # Parameters
283+ ///
282284 /// * iova: IO virtual address to mapping the memory.
283285 /// * size: size of the memory region.
284286 /// * 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 >( ) ) ;
286308 let dma_map = vfio_iommu_type1_dma_map {
287309 argsz : mem:: size_of :: < vfio_iommu_type1_dma_map > ( ) as u32 ,
288310 flags : VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE ,
289- vaddr : user_addr,
311+ vaddr : ( user_addr as usize ) . try_into ( ) . unwrap ( ) ,
290312 iova,
291- size,
313+ size : size . try_into ( ) . unwrap ( ) ,
292314 } ;
293315
294316 vfio_syscall:: map_dma ( self , & dma_map)
@@ -299,16 +321,16 @@ impl VfioContainer {
299321 /// # Parameters
300322 /// * iova: IO virtual address to mapping the memory.
301323 /// * 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 < ( ) > {
303325 let mut dma_unmap = vfio_iommu_type1_dma_unmap {
304326 argsz : mem:: size_of :: < vfio_iommu_type1_dma_unmap > ( ) as u32 ,
305327 flags : 0 ,
306328 iova,
307- size,
329+ size : size . try_into ( ) . unwrap ( ) ,
308330 } ;
309331
310332 vfio_syscall:: unmap_dma ( self , & mut dma_unmap) ?;
311- if dma_unmap. size != size {
333+ if dma_unmap. size != u64 :: try_from ( size) . unwrap ( ) {
312334 return Err ( VfioError :: InvalidDmaUnmapSize ) ;
313335 }
314336
@@ -319,29 +341,68 @@ impl VfioContainer {
319341 ///
320342 /// # Parameters
321343 /// * 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 < ( ) > {
323363 mem. iter ( ) . try_for_each ( |region| {
324364 let host_addr = region
325365 . get_host_address ( MemoryRegionAddress ( 0 ) )
326366 . 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+ }
332376 } )
333377 }
334378
335379 /// Remove all guest memory regions from the vfio container's iommu table.
336380 ///
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.
339391 ///
340392 /// # Parameters
341393 /// * 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.
342400 pub fn vfio_unmap_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
343401 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+ )
345406 } )
346407 }
347408
@@ -1358,8 +1419,10 @@ mod tests {
13581419 container. put_group ( group3) ;
13591420 assert_eq ! ( Arc :: strong_count( & group) , 1 ) ;
13601421
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 ( ) ;
13631426 container. vfio_dma_unmap ( 0x1000 , 0x1000 ) . unwrap ( ) ;
13641427 container. vfio_dma_unmap ( 0x2000 , 0x2000 ) . unwrap_err ( ) ;
13651428 }
@@ -1506,7 +1569,9 @@ mod tests {
15061569 let mem1 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr1, 0x1000 ) ] ) . unwrap ( ) ;
15071570 let container = create_vfio_container ( ) ;
15081571
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 ( ) ;
15101575
15111576 let addr2 = GuestAddress ( 0x3000 ) ;
15121577 let mem2 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr2, 0x1000 ) ] ) . unwrap ( ) ;
0 commit comments