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 as u64 { 169 return Err(Error::InvalidOffset); 170 } 171 172 let created_address = match addr { 173 Some(addr) => MapViewOfFileEx( 174 file_handle.0, 175 access_flags, 176 get_high_order(file_handle.1), 177 get_low_order(file_handle.1), 178 size, 179 addr as *mut c_void, 180 ), 181 None => MapViewOfFile( 182 file_handle.0, 183 access_flags, 184 get_high_order(file_handle.1), 185 get_low_order(file_handle.1), 186 size, 187 ), 188 }; 189 190 if created_address.is_null() { 191 return Err(Error::SystemCallFailed(super::Error::last())); 192 } 193 194 Ok(MemoryMapping { 195 addr: created_address, 196 size, 197 }) 198 } 199 200 /// Calls FlushViewOfFile on the mapped memory range, ensuring all changes that would 201 /// be written to disk are written immediately msync(&self) -> Result<()>202 pub fn msync(&self) -> Result<()> { 203 // SAFETY: 204 // Safe because self can only be created as a successful memory mapping 205 unsafe { 206 if FlushViewOfFile(self.addr, self.size) == 0 { 207 return Err(Error::SystemCallFailed(super::Error::last())); 208 } 209 }; 210 Ok(()) 211 } 212 } 213 214 impl Drop for MemoryMapping { drop(&mut self)215 fn drop(&mut self) { 216 // SAFETY: 217 // This is safe because we MapViewOfFile the area at addr ourselves, and nobody 218 // else is holding a reference to it. 219 unsafe { 220 if UnmapViewOfFile(self.addr) == 0 { 221 warn!("Unsuccessful unmap of file: {}", super::Error::last()); 222 } 223 } 224 } 225 } 226 227 /// TODO(b/150415526): This is currently an empty implementation to allow simple usages in 228 /// shared structs. Any attempts to use it will result in compiler errors. It will need 229 /// to be ported to actually be used on Windows. 230 pub struct MemoryMappingArena(); 231 232 #[cfg(test)] 233 mod tests { 234 use std::ptr; 235 236 use winapi::shared::winerror; 237 238 use super::super::pagesize; 239 use super::Error; 240 use crate::descriptor::FromRawDescriptor; 241 use crate::MappedRegion; 242 use crate::MemoryMappingBuilder; 243 use crate::SharedMemory; 244 245 #[test] map_invalid_fd()246 fn map_invalid_fd() { 247 // SAFETY: trivially safe to create an invalid File. 248 let descriptor = unsafe { std::fs::File::from_raw_descriptor(ptr::null_mut()) }; 249 let res = MemoryMappingBuilder::new(1024) 250 .from_file(&descriptor) 251 .build() 252 .unwrap_err(); 253 if let Error::StdSyscallFailed(e) = res { 254 assert_eq!( 255 e.raw_os_error(), 256 Some(winerror::ERROR_INVALID_HANDLE as i32) 257 ); 258 } else { 259 panic!("unexpected error: {}", res); 260 } 261 } 262 263 #[test] from_descriptor_offset_invalid()264 fn from_descriptor_offset_invalid() { 265 let shm = SharedMemory::new("test", 1028).unwrap(); 266 let res = MemoryMappingBuilder::new(4096) 267 .from_shared_memory(&shm) 268 .offset((i64::MAX as u64) + 1) 269 .build() 270 .unwrap_err(); 271 match res { 272 Error::InvalidOffset => {} 273 e => panic!("unexpected error: {}", e), 274 } 275 } 276 277 #[test] arena_msync()278 fn arena_msync() { 279 let size: usize = 0x40000; 280 let shm = SharedMemory::new("test", size as u64).unwrap(); 281 let m = MemoryMappingBuilder::new(size) 282 .from_shared_memory(&shm) 283 .build() 284 .unwrap(); 285 let ps = pagesize(); 286 <dyn MappedRegion>::msync(&m, 0, ps).unwrap(); 287 <dyn MappedRegion>::msync(&m, 0, size).unwrap(); 288 <dyn MappedRegion>::msync(&m, ps, size - ps).unwrap(); 289 let res = <dyn MappedRegion>::msync(&m, ps, size).unwrap_err(); 290 match res { 291 Error::InvalidAddress => {} 292 e => panic!("unexpected error: {}", e), 293 } 294 } 295 } 296