• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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