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,17 +321,17 @@ 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 ..Default :: default ( )
309331 } ;
310332
311333 vfio_syscall:: unmap_dma ( self , & mut dma_unmap) ?;
312- if dma_unmap. size != size {
334+ if dma_unmap. size != u64 :: try_from ( size) . unwrap ( ) {
313335 return Err ( VfioError :: InvalidDmaUnmapSize ) ;
314336 }
315337
@@ -320,29 +342,68 @@ impl VfioContainer {
320342 ///
321343 /// # Parameters
322344 /// * 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 < ( ) > {
324364 mem. iter ( ) . try_for_each ( |region| {
325365 let host_addr = region
326366 . get_host_address ( MemoryRegionAddress ( 0 ) )
327367 . 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+ }
333377 } )
334378 }
335379
336380 /// Remove all guest memory regions from the vfio container's iommu table.
337381 ///
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.
340392 ///
341393 /// # Parameters
342394 /// * 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.
343401 pub fn vfio_unmap_guest_memory < M : GuestMemory > ( & self , mem : & M ) -> Result < ( ) > {
344402 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+ )
346407 } )
347408 }
348409
@@ -1361,8 +1422,10 @@ mod tests {
13611422 container. put_group ( group3) ;
13621423 assert_eq ! ( Arc :: strong_count( & group) , 1 ) ;
13631424
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 ( ) ;
13661429 container. vfio_dma_unmap ( 0x1000 , 0x1000 ) . unwrap ( ) ;
13671430 container. vfio_dma_unmap ( 0x2000 , 0x2000 ) . unwrap_err ( ) ;
13681431 }
@@ -1509,7 +1572,9 @@ mod tests {
15091572 let mem1 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr1, 0x1000 ) ] ) . unwrap ( ) ;
15101573 let container = create_vfio_container ( ) ;
15111574
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 ( ) ;
15131578
15141579 let addr2 = GuestAddress ( 0x3000 ) ;
15151580 let mem2 = GuestMemoryMmap :: < ( ) > :: from_ranges ( & [ ( addr2, 0x1000 ) ] ) . unwrap ( ) ;
0 commit comments