xref: /aosp_15_r20/external/crosvm/base/src/sys/windows/mmap_platform.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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