1 // Copyright (C) 2019 CrowdStrike, Inc. All rights reserved.
2 // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
3 
4 //! Helper structure for working with mmaped memory regions in Windows.
5 
6 use std;
7 use std::io;
8 use std::os::windows::io::{AsRawHandle, RawHandle};
9 use std::ptr::{null, null_mut};
10 
11 use libc::{c_void, size_t};
12 
13 use winapi::um::errhandlingapi::GetLastError;
14 
15 use crate::bitmap::{Bitmap, BS};
16 use crate::guest_memory::FileOffset;
17 use crate::mmap::NewBitmap;
18 use crate::volatile_memory::{self, compute_offset, VolatileMemory, VolatileSlice};
19 
20 #[allow(non_snake_case)]
21 #[link(name = "kernel32")]
22 extern "stdcall" {
VirtualAlloc( lpAddress: *mut c_void, dwSize: size_t, flAllocationType: u32, flProtect: u32, ) -> *mut c_void23     pub fn VirtualAlloc(
24         lpAddress: *mut c_void,
25         dwSize: size_t,
26         flAllocationType: u32,
27         flProtect: u32,
28     ) -> *mut c_void;
29 
VirtualFree(lpAddress: *mut c_void, dwSize: size_t, dwFreeType: u32) -> u3230     pub fn VirtualFree(lpAddress: *mut c_void, dwSize: size_t, dwFreeType: u32) -> u32;
31 
CreateFileMappingA( hFile: RawHandle, lpFileMappingAttributes: *const c_void, flProtect: u32, dwMaximumSizeHigh: u32, dwMaximumSizeLow: u32, lpName: *const u8, ) -> RawHandle32     pub fn CreateFileMappingA(
33         hFile: RawHandle,                       // HANDLE
34         lpFileMappingAttributes: *const c_void, // LPSECURITY_ATTRIBUTES
35         flProtect: u32,                         // DWORD
36         dwMaximumSizeHigh: u32,                 // DWORD
37         dwMaximumSizeLow: u32,                  // DWORD
38         lpName: *const u8,                      // LPCSTR
39     ) -> RawHandle; // HANDLE
40 
MapViewOfFile( hFileMappingObject: RawHandle, dwDesiredAccess: u32, dwFileOffsetHigh: u32, dwFileOffsetLow: u32, dwNumberOfBytesToMap: size_t, ) -> *mut c_void41     pub fn MapViewOfFile(
42         hFileMappingObject: RawHandle,
43         dwDesiredAccess: u32,
44         dwFileOffsetHigh: u32,
45         dwFileOffsetLow: u32,
46         dwNumberOfBytesToMap: size_t,
47     ) -> *mut c_void;
48 
CloseHandle(hObject: RawHandle) -> u3249     pub fn CloseHandle(hObject: RawHandle) -> u32; // BOOL
50 }
51 
52 const MM_HIGHEST_VAD_ADDRESS: u64 = 0x000007FFFFFDFFFF;
53 
54 const MEM_COMMIT: u32 = 0x00001000;
55 const MEM_RELEASE: u32 = 0x00008000;
56 const FILE_MAP_ALL_ACCESS: u32 = 0xf001f;
57 const PAGE_READWRITE: u32 = 0x04;
58 
59 pub const MAP_FAILED: *mut c_void = 0 as *mut c_void;
60 pub const INVALID_HANDLE_VALUE: RawHandle = (-1isize) as RawHandle;
61 #[allow(dead_code)]
62 pub const ERROR_INVALID_PARAMETER: i32 = 87;
63 
64 /// Helper structure for working with mmaped memory regions in Unix.
65 ///
66 /// The structure is used for accessing the guest's physical memory by mmapping it into
67 /// the current process.
68 ///
69 /// # Limitations
70 /// When running a 64-bit virtual machine on a 32-bit hypervisor, only part of the guest's
71 /// physical memory may be mapped into the current process due to the limited virtual address
72 /// space size of the process.
73 #[derive(Debug)]
74 pub struct MmapRegion<B> {
75     addr: *mut u8,
76     size: usize,
77     bitmap: B,
78     file_offset: Option<FileOffset>,
79 }
80 
81 // Send and Sync aren't automatically inherited for the raw address pointer.
82 // Accessing that pointer is only done through the stateless interface which
83 // allows the object to be shared by multiple threads without a decrease in
84 // safety.
85 unsafe impl<B: Send> Send for MmapRegion<B> {}
86 unsafe impl<B: Sync> Sync for MmapRegion<B> {}
87 
88 impl<B: NewBitmap> MmapRegion<B> {
89     /// Creates a shared anonymous mapping of `size` bytes.
90     ///
91     /// # Arguments
92     /// * `size` - The size of the memory region in bytes.
new(size: usize) -> io::Result<Self>93     pub fn new(size: usize) -> io::Result<Self> {
94         if (size == 0) || (size > MM_HIGHEST_VAD_ADDRESS as usize) {
95             return Err(io::Error::from_raw_os_error(libc::EINVAL));
96         }
97         // This is safe because we are creating an anonymous mapping in a place not already used by
98         // any other area in this process.
99         let addr = unsafe { VirtualAlloc(0 as *mut c_void, size, MEM_COMMIT, PAGE_READWRITE) };
100         if addr == MAP_FAILED {
101             return Err(io::Error::last_os_error());
102         }
103         Ok(Self {
104             addr: addr as *mut u8,
105             size,
106             bitmap: B::with_len(size),
107             file_offset: None,
108         })
109     }
110 
111     /// Creates a shared file mapping of `size` bytes.
112     ///
113     /// # Arguments
114     /// * `file_offset` - The mapping will be created at offset `file_offset.start` in the file
115     ///                   referred to by `file_offset.file`.
116     /// * `size` - The size of the memory region in bytes.
from_file(file_offset: FileOffset, size: usize) -> io::Result<Self>117     pub fn from_file(file_offset: FileOffset, size: usize) -> io::Result<Self> {
118         let handle = file_offset.file().as_raw_handle();
119         if handle == INVALID_HANDLE_VALUE {
120             return Err(io::Error::from_raw_os_error(libc::EBADF));
121         }
122 
123         let mapping = unsafe {
124             CreateFileMappingA(
125                 handle,
126                 null(),
127                 PAGE_READWRITE,
128                 (size >> 32) as u32,
129                 size as u32,
130                 null(),
131             )
132         };
133         if mapping == 0 as RawHandle {
134             return Err(io::Error::last_os_error());
135         }
136 
137         let offset = file_offset.start();
138 
139         // This is safe because we are creating a mapping in a place not already used by any other
140         // area in this process.
141         let addr = unsafe {
142             MapViewOfFile(
143                 mapping,
144                 FILE_MAP_ALL_ACCESS,
145                 (offset >> 32) as u32,
146                 offset as u32,
147                 size,
148             )
149         };
150 
151         unsafe {
152             CloseHandle(mapping);
153         }
154 
155         if addr == null_mut() {
156             return Err(io::Error::last_os_error());
157         }
158         Ok(Self {
159             addr: addr as *mut u8,
160             size,
161             bitmap: B::with_len(size),
162             file_offset: Some(file_offset),
163         })
164     }
165 }
166 
167 impl<B: Bitmap> MmapRegion<B> {
168     /// Returns a pointer to the beginning of the memory region. Mutable accesses performed
169     /// using the resulting pointer are not automatically accounted for by the dirty bitmap
170     /// tracking functionality.
171     ///
172     /// Should only be used for passing this region to ioctls for setting guest memory.
as_ptr(&self) -> *mut u8173     pub fn as_ptr(&self) -> *mut u8 {
174         self.addr
175     }
176 
177     /// Returns the size of this region.
size(&self) -> usize178     pub fn size(&self) -> usize {
179         self.size
180     }
181 
182     /// Returns information regarding the offset into the file backing this region (if any).
file_offset(&self) -> Option<&FileOffset>183     pub fn file_offset(&self) -> Option<&FileOffset> {
184         self.file_offset.as_ref()
185     }
186 
187     /// Returns a reference to the inner bitmap object.
bitmap(&self) -> &B188     pub fn bitmap(&self) -> &B {
189         &self.bitmap
190     }
191 }
192 
193 impl<B: Bitmap> VolatileMemory for MmapRegion<B> {
194     type B = B;
195 
len(&self) -> usize196     fn len(&self) -> usize {
197         self.size
198     }
199 
get_slice( &self, offset: usize, count: usize, ) -> volatile_memory::Result<VolatileSlice<BS<Self::B>>>200     fn get_slice(
201         &self,
202         offset: usize,
203         count: usize,
204     ) -> volatile_memory::Result<VolatileSlice<BS<Self::B>>> {
205         let end = compute_offset(offset, count)?;
206         if end > self.size {
207             return Err(volatile_memory::Error::OutOfBounds { addr: end });
208         }
209 
210         // Safe because we checked that offset + count was within our range and we only ever hand
211         // out volatile accessors.
212         Ok(unsafe {
213             VolatileSlice::with_bitmap(
214                 self.addr.add(offset),
215                 count,
216                 self.bitmap.slice_at(offset),
217                 None,
218             )
219         })
220     }
221 }
222 
223 impl<B> Drop for MmapRegion<B> {
drop(&mut self)224     fn drop(&mut self) {
225         // This is safe because we mmap the area at addr ourselves, and nobody
226         // else is holding a reference to it.
227         // Note that the size must be set to 0 when using MEM_RELEASE,
228         // otherwise the function fails.
229         unsafe {
230             let ret_val = VirtualFree(self.addr as *mut libc::c_void, 0, MEM_RELEASE);
231             if ret_val == 0 {
232                 let err = GetLastError();
233                 // We can't use any fancy logger here, yet we want to
234                 // pin point memory leaks.
235                 println!(
236                     "WARNING: Could not deallocate mmap region. \
237                      Address: {:?}. Size: {}. Error: {}",
238                     self.addr, self.size, err
239                 )
240             }
241         }
242     }
243 }
244 
245 #[cfg(test)]
246 mod tests {
247     use std::os::windows::io::FromRawHandle;
248 
249     use crate::bitmap::AtomicBitmap;
250     use crate::guest_memory::FileOffset;
251     use crate::mmap_windows::INVALID_HANDLE_VALUE;
252 
253     type MmapRegion = super::MmapRegion<()>;
254 
255     #[test]
map_invalid_handle()256     fn map_invalid_handle() {
257         let file = unsafe { std::fs::File::from_raw_handle(INVALID_HANDLE_VALUE) };
258         let file_offset = FileOffset::new(file, 0);
259         let e = MmapRegion::from_file(file_offset, 1024).unwrap_err();
260         assert_eq!(e.raw_os_error(), Some(libc::EBADF));
261     }
262 
263     #[test]
test_dirty_tracking()264     fn test_dirty_tracking() {
265         // Using the `crate` prefix because we aliased `MmapRegion` to `MmapRegion<()>` for
266         // the rest of the unit tests above.
267         let m = crate::MmapRegion::<AtomicBitmap>::new(0x1_0000).unwrap();
268         crate::bitmap::tests::test_volatile_memory(&m);
269     }
270 }
271