1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! The mmap module provides a safe interface to map memory and ensures UnmapViewOfFile is called 6 //! when the mmap object leaves scope. 7 8 use libc::c_void; 9 use win_util::get_high_order; 10 use win_util::get_low_order; 11 use winapi::shared::minwindef::DWORD; 12 use winapi::um::memoryapi::FlushViewOfFile; 13 use winapi::um::memoryapi::MapViewOfFile; 14 use winapi::um::memoryapi::MapViewOfFileEx; 15 use winapi::um::memoryapi::UnmapViewOfFile; 16 use winapi::um::memoryapi::FILE_MAP_READ; 17 use winapi::um::memoryapi::FILE_MAP_WRITE; 18 19 use super::allocation_granularity; 20 use super::mmap::MemoryMapping; 21 use crate::descriptor::AsRawDescriptor; 22 use crate::warn; 23 use crate::MmapError as Error; 24 use crate::MmapResult as Result; 25 use crate::Protection; 26 use crate::RawDescriptor; 27 28 impl From<Protection> for DWORD { 29 #[inline(always)] from(p: Protection) -> Self30 fn from(p: Protection) -> Self { 31 let mut value = 0; 32 if p.read { 33 value |= FILE_MAP_READ; 34 } 35 if p.write { 36 value |= FILE_MAP_WRITE; 37 } 38 value 39 } 40 } 41 42 impl MemoryMapping { 43 /// Creates an anonymous shared mapping of `size` bytes with `prot` protection. 44 /// 45 /// # Arguments 46 /// * `size` - Size of memory region in bytes. 47 /// * `prot` - Protection (e.g. readable/writable) of the memory region. new_protection(size: usize, prot: Protection) -> Result<MemoryMapping>48 pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> { 49 // SAFETY: 50 // This is safe because we are creating an anonymous mapping in a place not already used by 51 // any other area in this process. 52 unsafe { MemoryMapping::try_mmap(None, size, prot.into(), None) } 53 } 54 55 /// Create a Memory mapping from a raw address returned by 56 /// the kernel. 57 /// # Arguments 58 /// * `addr` - Raw pointer to memory region. 59 /// * `size` - Size of memory region in bytes. from_raw_ptr(addr: *mut c_void, size: usize) -> Result<MemoryMapping>60 pub fn from_raw_ptr(addr: *mut c_void, size: usize) -> Result<MemoryMapping> { 61 Ok(MemoryMapping { addr, size }) 62 } 63 from_raw_handle_offset( file_handle: RawDescriptor, size: usize, ) -> Result<MemoryMapping>64 pub fn from_raw_handle_offset( 65 file_handle: RawDescriptor, 66 size: usize, 67 ) -> Result<MemoryMapping> { 68 // SAFETY: 69 // This is safe because we are creating an anonymous mapping in a place not already used by 70 // any other area in this process. 71 unsafe { 72 MemoryMapping::try_mmap( 73 None, 74 size, 75 Protection::read_write().into(), 76 Some((file_handle, 0)), 77 ) 78 } 79 } 80 81 /// Maps the `size` bytes starting at `offset` bytes of the given `descriptor` as read/write. 82 /// 83 /// # Arguments 84 /// * `file_handle` - File handle to map from. 85 /// * `size` - Size of memory region in bytes. 86 /// * `offset` - Offset in bytes from the beginning of `descriptor` to start the mmap. 87 /// * `prot` - Protection (e.g. readable/writable) of the memory region. from_descriptor_offset_protection( file_handle: &dyn AsRawDescriptor, size: usize, offset: u64, prot: Protection, ) -> Result<MemoryMapping>88 pub fn from_descriptor_offset_protection( 89 file_handle: &dyn AsRawDescriptor, 90 size: usize, 91 offset: u64, 92 prot: Protection, 93 ) -> Result<MemoryMapping> { 94 // SAFETY: 95 // This is safe because we are creating an anonymous mapping in a place not already used by 96 // any other area in this process. 97 unsafe { 98 MemoryMapping::try_mmap( 99 None, 100 size, 101 prot.into(), 102 Some((file_handle.as_raw_descriptor(), offset)), 103 ) 104 } 105 } 106 107 /// Creates an anonymous shared mapping of `size` bytes with `prot` protection. 108 /// 109 /// # Safety 110 /// Unsafe: unmaps any mmap'd regions already present at (addr..addr+size). 111 /// 112 /// # Arguments 113 /// * `addr` - Memory address to mmap at. 114 /// * `size` - Size of memory region in bytes. 115 /// * `prot` - Protection (e.g. readable/writable) of the memory region. new_protection_fixed( addr: *mut u8, size: usize, prot: Protection, ) -> Result<MemoryMapping>116 pub unsafe fn new_protection_fixed( 117 addr: *mut u8, 118 size: usize, 119 prot: Protection, 120 ) -> Result<MemoryMapping> { 121 MemoryMapping::try_mmap(Some(addr), size, prot.into(), None) 122 } 123 124 /// Maps the `size` bytes starting at `offset` bytes of the given `descriptor` with 125 /// `prot` protections. 126 /// 127 /// # Safety 128 /// Unsafe: unmaps any mmap'd regions already present at (addr..addr+size). 129 /// 130 /// # Arguments 131 /// * `addr` - Memory address to mmap at. 132 /// * `descriptor` - File descriptor to mmap from. 133 /// * `size` - Size of memory region in bytes. 134 /// * `offset` - Offset in bytes from the beginning of `descriptor` to start the mmap. 135 /// * `prot` - Protection (e.g. readable/writable) of the memory region. from_descriptor_offset_protection_fixed( addr: *mut u8, descriptor: &dyn AsRawDescriptor, size: usize, offset: u64, prot: Protection, ) -> Result<MemoryMapping>136 pub unsafe fn from_descriptor_offset_protection_fixed( 137 addr: *mut u8, 138 descriptor: &dyn AsRawDescriptor, 139 size: usize, 140 offset: u64, 141 prot: Protection, 142 ) -> Result<MemoryMapping> { 143 MemoryMapping::try_mmap( 144 Some(addr), 145 size, 146 prot.into(), 147 Some((descriptor.as_raw_descriptor(), offset)), 148 ) 149 } 150 151 /// Calls MapViewOfFile/MapViewOfFileEx with the parameters given try_mmap( addr: Option<*mut u8>, size: usize, access_flags: DWORD, file_handle: Option<(RawDescriptor, u64)>, ) -> Result<MemoryMapping>152 unsafe fn try_mmap( 153 addr: Option<*mut u8>, 154 size: usize, 155 access_flags: DWORD, 156 file_handle: Option<(RawDescriptor, u64)>, 157 ) -> Result<MemoryMapping> { 158 if file_handle.is_none() { 159 // TODO(mikehoyle): For now, do not allow the no-handle scenario. As per comments 160 // in guest_memory, the no-handle case is for legacy support and shouldn't have 161 // significant usage. 162 return Err(Error::InvalidAddress); 163 } 164 let file_handle = file_handle.unwrap(); 165 166 // on windows, pages needed to be of fixed granular size, and the 167 // maximum valid value is an i64. 168 if file_handle.1 % allocation_granularity() != 0 || file_handle.1 > i64::max_value() as u64 169 { 170 return Err(Error::InvalidOffset); 171 } 172 173 let created_address = match addr { 174 Some(addr) => MapViewOfFileEx( 175 file_handle.0, 176 access_flags, 177 get_high_order(file_handle.1), 178 get_low_order(file_handle.1), 179 size, 180 addr as *mut c_void, 181 ), 182 None => MapViewOfFile( 183 file_handle.0, 184 access_flags, 185 get_high_order(file_handle.1), 186 get_low_order(file_handle.1), 187 size, 188 ), 189 }; 190 191 if created_address.is_null() { 192 return Err(Error::SystemCallFailed(super::Error::last())); 193 } 194 195 Ok(MemoryMapping { 196 addr: created_address, 197 size, 198 }) 199 } 200 201 /// Calls FlushViewOfFile on the mapped memory range, ensuring all changes that would 202 /// be written to disk are written immediately msync(&self) -> Result<()>203 pub fn msync(&self) -> Result<()> { 204 // SAFETY: 205 // Safe because self can only be created as a successful memory mapping 206 unsafe { 207 if FlushViewOfFile(self.addr, self.size) == 0 { 208 return Err(Error::SystemCallFailed(super::Error::last())); 209 } 210 }; 211 Ok(()) 212 } 213 } 214 215 impl Drop for MemoryMapping { drop(&mut self)216 fn drop(&mut self) { 217 // SAFETY: 218 // This is safe because we MapViewOfFile the area at addr ourselves, and nobody 219 // else is holding a reference to it. 220 unsafe { 221 if UnmapViewOfFile(self.addr) == 0 { 222 warn!("Unsuccessful unmap of file: {}", super::Error::last()); 223 } 224 } 225 } 226 } 227 228 /// TODO(b/150415526): This is currently an empty implementation to allow simple usages in 229 /// shared structs. Any attempts to use it will result in compiler errors. It will need 230 /// to be ported to actually be used on Windows. 231 pub struct MemoryMappingArena(); 232 233 #[cfg(test)] 234 mod tests { 235 use std::ptr; 236 237 use winapi::shared::winerror; 238 239 use super::super::pagesize; 240 use super::Error; 241 use crate::descriptor::FromRawDescriptor; 242 use crate::MappedRegion; 243 use crate::MemoryMappingBuilder; 244 use crate::SharedMemory; 245 246 #[test] map_invalid_fd()247 fn map_invalid_fd() { 248 // SAFETY: trivially safe to create an invalid File. 249 let descriptor = unsafe { std::fs::File::from_raw_descriptor(ptr::null_mut()) }; 250 let res = MemoryMappingBuilder::new(1024) 251 .from_file(&descriptor) 252 .build() 253 .unwrap_err(); 254 if let Error::StdSyscallFailed(e) = res { 255 assert_eq!( 256 e.raw_os_error(), 257 Some(winerror::ERROR_INVALID_HANDLE as i32) 258 ); 259 } else { 260 panic!("unexpected error: {}", res); 261 } 262 } 263 264 #[test] from_descriptor_offset_invalid()265 fn from_descriptor_offset_invalid() { 266 let shm = SharedMemory::new("test", 1028).unwrap(); 267 let res = MemoryMappingBuilder::new(4096) 268 .from_shared_memory(&shm) 269 .offset((i64::max_value() as u64) + 1) 270 .build() 271 .unwrap_err(); 272 match res { 273 Error::InvalidOffset => {} 274 e => panic!("unexpected error: {}", e), 275 } 276 } 277 278 #[test] arena_msync()279 fn arena_msync() { 280 let size: usize = 0x40000; 281 let shm = SharedMemory::new("test", size as u64).unwrap(); 282 let m = MemoryMappingBuilder::new(size) 283 .from_shared_memory(&shm) 284 .build() 285 .unwrap(); 286 let ps = pagesize(); 287 <dyn MappedRegion>::msync(&m, 0, ps).unwrap(); 288 <dyn MappedRegion>::msync(&m, 0, size).unwrap(); 289 <dyn MappedRegion>::msync(&m, ps, size - ps).unwrap(); 290 let res = <dyn MappedRegion>::msync(&m, ps, size).unwrap_err(); 291 match res { 292 Error::InvalidAddress => {} 293 e => panic!("unexpected error: {}", e), 294 } 295 } 296 } 297