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,36 @@ 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)`, it is not safe to use any of the address range in
294+ /// `[user_addr..user_addr+size]` for almost any purpose. The only permitted purposes are
295+ ///
296+ /// - Atomic and/or volatile operations.
297+ /// - Assignment to a guest.
298+ /// - Sharing with another process.
299+ /// - Passing a raw pointer to functions that only do one of the above things.
300+ ///
301+ /// In particular, creating a Rust reference to this memory is instant undefined behavior
302+ /// due to the Rust aliasing rules. It is also undefined behavior to call this function if
303+ /// a Rust reference to this memory is live.
304+ pub unsafe fn vfio_dma_map ( & self , iova : u64 , size : usize , user_addr : * mut u8 ) -> Result < ( ) > {
305+ const _: ( ) = assert ! ( mem:: size_of:: <u64 >( ) >= mem:: size_of:: <* mut u8 >( ) ) ;
306+ const _: ( ) = assert ! ( mem:: size_of:: <u64 >( ) >= mem:: size_of:: <usize >( ) ) ;
286307 let dma_map = vfio_iommu_type1_dma_map {
287308 argsz : mem:: size_of :: < vfio_iommu_type1_dma_map > ( ) as u32 ,
288309 flags : VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE ,
289- vaddr : user_addr,
310+ vaddr : ( user_addr as usize ) . try_into ( ) . unwrap ( ) ,
290311 iova,
291- size,
312+ size : size . try_into ( ) . unwrap ( ) ,
292313 } ;
293314
294315 vfio_syscall:: map_dma ( self , & dma_map)
@@ -299,16 +320,16 @@ impl VfioContainer {
299320 /// # Parameters
300321 /// * iova: IO virtual address to mapping the memory.
301322 /// * size: size of the memory region.
302- pub fn vfio_dma_unmap ( & self , iova : u64 , size : u64 ) -> Result < ( ) > {
323+ pub fn vfio_dma_unmap ( & self , iova : u64 , size : usize ) -> Result < ( ) > {
303324 let mut dma_unmap = vfio_iommu_type1_dma_unmap {
304325 argsz : mem:: size_of :: < vfio_iommu_type1_dma_unmap > ( ) as u32 ,
305326 flags : 0 ,
306327 iova,
307- size,
328+ size : size . try_into ( ) . unwrap ( ) ,
308329 } ;
309330
310331 vfio_syscall:: unmap_dma ( self , & mut dma_unmap) ?;
311- if dma_unmap. size != size {
332+ if dma_unmap. size != u64 :: try_from ( size) . unwrap ( ) {
312333 return Err ( VfioError :: InvalidDmaUnmapSize ) ;
313334 }
314335
@@ -319,29 +340,54 @@ impl VfioContainer {
319340 ///
320341 /// # Parameters
321342 /// * 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 < ( ) > {
343+ ///
344+ /// # Safety
345+ ///
346+ /// You have to promise that the requirements of [`Self::vfio_dma_map`] are upheld for each
347+ /// of the regions. A future version of the crate will ensure that they are upheld by having
348+ /// `vfio-ioctls` own a reference to the memory mapped into the guest. You also have to
349+ /// promise that the [`GuestMemory`] implementation is well-behaved and upholds the contracts
350+ /// in its documentation.
351+ ///
352+ /// # Panics
353+ ///
354+ /// Panics if the length of one of the regions overflows `usize`.
355+ pub unsafe fn vfio_map_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
323356 mem. iter ( ) . try_for_each ( |region| {
324357 let host_addr = region
325358 . get_host_address ( MemoryRegionAddress ( 0 ) )
326359 . map_err ( |_| VfioError :: GetHostAddress ) ?;
327- self . vfio_dma_map (
328- region. start_addr ( ) . raw_value ( ) ,
329- region. len ( ) ,
330- host_addr as u64 ,
331- )
360+ // SAFETY: GuestMemory guarantees the requirements
361+ // are upheld.
362+ unsafe {
363+ self . vfio_dma_map (
364+ region. start_addr ( ) . raw_value ( ) ,
365+ region. len ( ) . try_into ( ) . unwrap ( ) ,
366+ host_addr,
367+ )
368+ }
332369 } )
333370 }
334371
335372 /// Remove all guest memory regions from the vfio container's iommu table.
336373 ///
337- /// The vfio kernel driver and device hardware couldn 't access this guest memory after
338- /// returning from the function.
374+ /// The vfio kernel driver and device hardware can 't access this guest memory after
375+ /// the function returns successfully .
339376 ///
340377 /// # Parameters
341378 /// * mem: pinned guest memory which could be accessed by devices binding to the container.
379+ ///
380+ /// # Panics
381+ ///
382+ /// Panics if the length of any of the regions overflows `usize`. That should have been
383+ /// caught by [`Self::vfio_map_guest_memory`], so it indicates a bogus [`GuestMemory`]
384+ /// implementation.
342385 pub fn vfio_unmap_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
343386 mem. iter ( ) . try_for_each ( |region| {
344- self . vfio_dma_unmap ( region. start_addr ( ) . raw_value ( ) , region. len ( ) )
387+ self . vfio_dma_unmap (
388+ region. start_addr ( ) . raw_value ( ) ,
389+ region. len ( ) . try_into ( ) . unwrap ( ) ,
390+ )
345391 } )
346392 }
347393
@@ -1358,8 +1404,10 @@ mod tests {
13581404 container. put_group ( group3) ;
13591405 assert_eq ! ( Arc :: strong_count( & group) , 1 ) ;
13601406
1361- container. vfio_dma_map ( 0x1000 , 0x1000 , 0x8000 ) . unwrap ( ) ;
1362- container. vfio_dma_map ( 0x2000 , 0x2000 , 0x8000 ) . unwrap_err ( ) ;
1407+ // SAFETY: this is a test implementation that does not access memory
1408+ unsafe { container. vfio_dma_map ( 0x1000 , 0x1000 , 0x8000 as _ ) } . unwrap ( ) ;
1409+ // SAFETY: this is a test implementation that does not access memory
1410+ unsafe { container. vfio_dma_map ( 0x2000 , 0x2000 , 0x8000 as _ ) } . unwrap_err ( ) ;
13631411 container. vfio_dma_unmap ( 0x1000 , 0x1000 ) . unwrap ( ) ;
13641412 container. vfio_dma_unmap ( 0x2000 , 0x2000 ) . unwrap_err ( ) ;
13651413 }
@@ -1506,7 +1554,9 @@ mod tests {
15061554 let mem1 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr1, 0x1000 ) ] ) . unwrap ( ) ;
15071555 let container = create_vfio_container ( ) ;
15081556
1509- container. vfio_map_guest_memory ( & mem1) . unwrap ( ) ;
1557+ // SAFETY: This is a dummy implementation that does not access
1558+ // memory.
1559+ unsafe { container. vfio_map_guest_memory ( & mem1) } . unwrap ( ) ;
15101560
15111561 let addr2 = GuestAddress ( 0x3000 ) ;
15121562 let mem2 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr2, 0x1000 ) ] ) . unwrap ( ) ;
0 commit comments