• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![allow(non_camel_case_types)]
2 #![allow(non_snake_case)]
3 
4 use std::fs::File;
5 use std::mem::ManuallyDrop;
6 use std::os::raw::c_void;
7 use std::os::windows::io::{FromRawHandle, RawHandle};
8 use std::{io, mem, ptr};
9 
10 type BOOL = i32;
11 type WORD = u16;
12 type DWORD = u32;
13 type WCHAR = u16;
14 type HANDLE = *mut c_void;
15 type LPHANDLE = *mut HANDLE;
16 type LPVOID = *mut c_void;
17 type LPCVOID = *const c_void;
18 type ULONG_PTR = usize;
19 type SIZE_T = ULONG_PTR;
20 type LPCWSTR = *const WCHAR;
21 type PDWORD = *mut DWORD;
22 type DWORD_PTR = ULONG_PTR;
23 type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES;
24 type LPSYSTEM_INFO = *mut SYSTEM_INFO;
25 
26 const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
27 
28 const DUPLICATE_SAME_ACCESS: DWORD = 0x00000002;
29 
30 const STANDARD_RIGHTS_REQUIRED: DWORD = 0x000F0000;
31 
32 const SECTION_QUERY: DWORD = 0x0001;
33 const SECTION_MAP_WRITE: DWORD = 0x0002;
34 const SECTION_MAP_READ: DWORD = 0x0004;
35 const SECTION_MAP_EXECUTE: DWORD = 0x0008;
36 const SECTION_EXTEND_SIZE: DWORD = 0x0010;
37 const SECTION_MAP_EXECUTE_EXPLICIT: DWORD = 0x0020;
38 const SECTION_ALL_ACCESS: DWORD = STANDARD_RIGHTS_REQUIRED
39     | SECTION_QUERY
40     | SECTION_MAP_WRITE
41     | SECTION_MAP_READ
42     | SECTION_MAP_EXECUTE
43     | SECTION_EXTEND_SIZE;
44 
45 const PAGE_READONLY: DWORD = 0x02;
46 const PAGE_READWRITE: DWORD = 0x04;
47 const PAGE_WRITECOPY: DWORD = 0x08;
48 const PAGE_EXECUTE_READ: DWORD = 0x20;
49 const PAGE_EXECUTE_READWRITE: DWORD = 0x40;
50 const PAGE_EXECUTE_WRITECOPY: DWORD = 0x80;
51 
52 const FILE_MAP_WRITE: DWORD = SECTION_MAP_WRITE;
53 const FILE_MAP_READ: DWORD = SECTION_MAP_READ;
54 const FILE_MAP_ALL_ACCESS: DWORD = SECTION_ALL_ACCESS;
55 const FILE_MAP_EXECUTE: DWORD = SECTION_MAP_EXECUTE_EXPLICIT;
56 const FILE_MAP_COPY: DWORD = 0x00000001;
57 
58 #[repr(C)]
59 struct SECURITY_ATTRIBUTES {
60     nLength: DWORD,
61     lpSecurityDescriptor: LPVOID,
62     bInheritHandle: BOOL,
63 }
64 
65 #[repr(C)]
66 struct SYSTEM_INFO {
67     wProcessorArchitecture: WORD,
68     wReserved: WORD,
69     dwPageSize: DWORD,
70     lpMinimumApplicationAddress: LPVOID,
71     lpMaximumApplicationAddress: LPVOID,
72     dwActiveProcessorMask: DWORD_PTR,
73     dwNumberOfProcessors: DWORD,
74     dwProcessorType: DWORD,
75     dwAllocationGranularity: DWORD,
76     wProcessorLevel: WORD,
77     wProcessorRevision: WORD,
78 }
79 
80 #[repr(C)]
81 #[derive(Copy, Clone)]
82 pub struct FILETIME {
83     pub dwLowDateTime: DWORD,
84     pub dwHighDateTime: DWORD,
85 }
86 
87 extern "system" {
GetCurrentProcess() -> HANDLE88     fn GetCurrentProcess() -> HANDLE;
89 
CloseHandle(hObject: HANDLE) -> BOOL90     fn CloseHandle(hObject: HANDLE) -> BOOL;
91 
DuplicateHandle( hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, hTargetProcessHandle: HANDLE, lpTargetHandle: LPHANDLE, dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwOptions: DWORD, ) -> BOOL92     fn DuplicateHandle(
93         hSourceProcessHandle: HANDLE,
94         hSourceHandle: HANDLE,
95         hTargetProcessHandle: HANDLE,
96         lpTargetHandle: LPHANDLE,
97         dwDesiredAccess: DWORD,
98         bInheritHandle: BOOL,
99         dwOptions: DWORD,
100     ) -> BOOL;
101 
CreateFileMappingW( hFile: HANDLE, lpFileMappingAttributes: LPSECURITY_ATTRIBUTES, flProtect: DWORD, dwMaximumSizeHigh: DWORD, dwMaximumSizeLow: DWORD, lpName: LPCWSTR, ) -> HANDLE102     fn CreateFileMappingW(
103         hFile: HANDLE,
104         lpFileMappingAttributes: LPSECURITY_ATTRIBUTES,
105         flProtect: DWORD,
106         dwMaximumSizeHigh: DWORD,
107         dwMaximumSizeLow: DWORD,
108         lpName: LPCWSTR,
109     ) -> HANDLE;
110 
FlushFileBuffers(hFile: HANDLE) -> BOOL111     fn FlushFileBuffers(hFile: HANDLE) -> BOOL;
112 
FlushViewOfFile(lpBaseAddress: LPCVOID, dwNumberOfBytesToFlush: SIZE_T) -> BOOL113     fn FlushViewOfFile(lpBaseAddress: LPCVOID, dwNumberOfBytesToFlush: SIZE_T) -> BOOL;
114 
UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL115     fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL;
116 
MapViewOfFile( hFileMappingObject: HANDLE, dwDesiredAccess: DWORD, dwFileOffsetHigh: DWORD, dwFileOffsetLow: DWORD, dwNumberOfBytesToMap: SIZE_T, ) -> LPVOID117     fn MapViewOfFile(
118         hFileMappingObject: HANDLE,
119         dwDesiredAccess: DWORD,
120         dwFileOffsetHigh: DWORD,
121         dwFileOffsetLow: DWORD,
122         dwNumberOfBytesToMap: SIZE_T,
123     ) -> LPVOID;
124 
VirtualProtect( lpAddress: LPVOID, dwSize: SIZE_T, flNewProtect: DWORD, lpflOldProtect: PDWORD, ) -> BOOL125     fn VirtualProtect(
126         lpAddress: LPVOID,
127         dwSize: SIZE_T,
128         flNewProtect: DWORD,
129         lpflOldProtect: PDWORD,
130     ) -> BOOL;
131 
GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO)132     fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
133 }
134 
135 /// Returns a fixed aligned pointer that is valid for `slice::from_raw_parts::<u8>` with `len == 0`.
136 ///
137 /// This aligns the pointer to `allocation_granularity()` or 1 if unknown.
empty_slice_ptr() -> *mut c_void138 fn empty_slice_ptr() -> *mut c_void {
139     let align = allocation_granularity().max(1);
140     unsafe { mem::transmute(align) }
141 }
142 
143 pub struct MmapInner {
144     handle: Option<RawHandle>,
145     ptr: *mut c_void,
146     len: usize,
147     copy: bool,
148 }
149 
150 impl MmapInner {
151     /// Creates a new `MmapInner`.
152     ///
153     /// This is a thin wrapper around the `CreateFileMappingW` and `MapViewOfFile` system calls.
new( handle: RawHandle, protect: DWORD, access: DWORD, offset: u64, len: usize, copy: bool, ) -> io::Result<MmapInner>154     pub fn new(
155         handle: RawHandle,
156         protect: DWORD,
157         access: DWORD,
158         offset: u64,
159         len: usize,
160         copy: bool,
161     ) -> io::Result<MmapInner> {
162         let alignment = offset % allocation_granularity() as u64;
163         let aligned_offset = offset - alignment as u64;
164         let aligned_len = len + alignment as usize;
165         if aligned_len == 0 {
166             // `CreateFileMappingW` documents:
167             //
168             // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw
169             // > An attempt to map a file with a length of 0 (zero) fails with an error code
170             // > of ERROR_FILE_INVALID. Applications should test for files with a length of 0
171             // > (zero) and reject those files.
172             //
173             // For such files, don’t create a mapping at all and use a marker pointer instead.
174             return Ok(MmapInner {
175                 handle: None,
176                 ptr: empty_slice_ptr(),
177                 len: 0,
178                 copy,
179             });
180         }
181 
182         unsafe {
183             let mapping = CreateFileMappingW(handle, ptr::null_mut(), protect, 0, 0, ptr::null());
184             if mapping.is_null() {
185                 return Err(io::Error::last_os_error());
186             }
187 
188             let ptr = MapViewOfFile(
189                 mapping,
190                 access,
191                 (aligned_offset >> 16 >> 16) as DWORD,
192                 (aligned_offset & 0xffffffff) as DWORD,
193                 aligned_len as SIZE_T,
194             );
195             CloseHandle(mapping);
196             if ptr.is_null() {
197                 return Err(io::Error::last_os_error());
198             }
199 
200             let mut new_handle = 0 as RawHandle;
201             let cur_proc = GetCurrentProcess();
202             let ok = DuplicateHandle(
203                 cur_proc,
204                 handle,
205                 cur_proc,
206                 &mut new_handle,
207                 0,
208                 0,
209                 DUPLICATE_SAME_ACCESS,
210             );
211             if ok == 0 {
212                 UnmapViewOfFile(ptr);
213                 return Err(io::Error::last_os_error());
214             }
215 
216             Ok(MmapInner {
217                 handle: Some(new_handle),
218                 ptr: ptr.offset(alignment as isize),
219                 len: len as usize,
220                 copy,
221             })
222         }
223     }
224 
map( len: usize, handle: RawHandle, offset: u64, _populate: bool, ) -> io::Result<MmapInner>225     pub fn map(
226         len: usize,
227         handle: RawHandle,
228         offset: u64,
229         _populate: bool,
230     ) -> io::Result<MmapInner> {
231         let write = protection_supported(handle, PAGE_READWRITE);
232         let exec = protection_supported(handle, PAGE_EXECUTE_READ);
233         let mut access = FILE_MAP_READ;
234         let protection = match (write, exec) {
235             (true, true) => {
236                 access |= FILE_MAP_WRITE | FILE_MAP_EXECUTE;
237                 PAGE_EXECUTE_READWRITE
238             }
239             (true, false) => {
240                 access |= FILE_MAP_WRITE;
241                 PAGE_READWRITE
242             }
243             (false, true) => {
244                 access |= FILE_MAP_EXECUTE;
245                 PAGE_EXECUTE_READ
246             }
247             (false, false) => PAGE_READONLY,
248         };
249 
250         let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?;
251         if write || exec {
252             inner.make_read_only()?;
253         }
254         Ok(inner)
255     }
256 
map_exec( len: usize, handle: RawHandle, offset: u64, _populate: bool, ) -> io::Result<MmapInner>257     pub fn map_exec(
258         len: usize,
259         handle: RawHandle,
260         offset: u64,
261         _populate: bool,
262     ) -> io::Result<MmapInner> {
263         let write = protection_supported(handle, PAGE_READWRITE);
264         let mut access = FILE_MAP_READ | FILE_MAP_EXECUTE;
265         let protection = if write {
266             access |= FILE_MAP_WRITE;
267             PAGE_EXECUTE_READWRITE
268         } else {
269             PAGE_EXECUTE_READ
270         };
271 
272         let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?;
273         if write {
274             inner.make_exec()?;
275         }
276         Ok(inner)
277     }
278 
map_mut( len: usize, handle: RawHandle, offset: u64, _populate: bool, ) -> io::Result<MmapInner>279     pub fn map_mut(
280         len: usize,
281         handle: RawHandle,
282         offset: u64,
283         _populate: bool,
284     ) -> io::Result<MmapInner> {
285         let exec = protection_supported(handle, PAGE_EXECUTE_READ);
286         let mut access = FILE_MAP_READ | FILE_MAP_WRITE;
287         let protection = if exec {
288             access |= FILE_MAP_EXECUTE;
289             PAGE_EXECUTE_READWRITE
290         } else {
291             PAGE_READWRITE
292         };
293 
294         let mut inner = MmapInner::new(handle, protection, access, offset, len, false)?;
295         if exec {
296             inner.make_mut()?;
297         }
298         Ok(inner)
299     }
300 
map_copy( len: usize, handle: RawHandle, offset: u64, _populate: bool, ) -> io::Result<MmapInner>301     pub fn map_copy(
302         len: usize,
303         handle: RawHandle,
304         offset: u64,
305         _populate: bool,
306     ) -> io::Result<MmapInner> {
307         let exec = protection_supported(handle, PAGE_EXECUTE_READWRITE);
308         let mut access = FILE_MAP_COPY;
309         let protection = if exec {
310             access |= FILE_MAP_EXECUTE;
311             PAGE_EXECUTE_WRITECOPY
312         } else {
313             PAGE_WRITECOPY
314         };
315 
316         let mut inner = MmapInner::new(handle, protection, access, offset, len, true)?;
317         if exec {
318             inner.make_mut()?;
319         }
320         Ok(inner)
321     }
322 
map_copy_read_only( len: usize, handle: RawHandle, offset: u64, _populate: bool, ) -> io::Result<MmapInner>323     pub fn map_copy_read_only(
324         len: usize,
325         handle: RawHandle,
326         offset: u64,
327         _populate: bool,
328     ) -> io::Result<MmapInner> {
329         let write = protection_supported(handle, PAGE_READWRITE);
330         let exec = protection_supported(handle, PAGE_EXECUTE_READ);
331         let mut access = FILE_MAP_COPY;
332         let protection = if exec {
333             access |= FILE_MAP_EXECUTE;
334             PAGE_EXECUTE_WRITECOPY
335         } else {
336             PAGE_WRITECOPY
337         };
338 
339         let mut inner = MmapInner::new(handle, protection, access, offset, len, true)?;
340         if write || exec {
341             inner.make_read_only()?;
342         }
343         Ok(inner)
344     }
345 
map_anon(len: usize, _stack: bool, _populate: bool) -> io::Result<MmapInner>346     pub fn map_anon(len: usize, _stack: bool, _populate: bool) -> io::Result<MmapInner> {
347         // Ensure a non-zero length for the underlying mapping
348         let mapped_len = len.max(1);
349         unsafe {
350             // Create a mapping and view with maximum access permissions, then use `VirtualProtect`
351             // to set the actual `Protection`. This way, we can set more permissive protection later
352             // on.
353             // Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx
354 
355             let mapping = CreateFileMappingW(
356                 INVALID_HANDLE_VALUE,
357                 ptr::null_mut(),
358                 PAGE_EXECUTE_READWRITE,
359                 (mapped_len >> 16 >> 16) as DWORD,
360                 (mapped_len & 0xffffffff) as DWORD,
361                 ptr::null(),
362             );
363             if mapping.is_null() {
364                 return Err(io::Error::last_os_error());
365             }
366             let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
367             let ptr = MapViewOfFile(mapping, access, 0, 0, mapped_len as SIZE_T);
368             CloseHandle(mapping);
369 
370             if ptr.is_null() {
371                 return Err(io::Error::last_os_error());
372             }
373 
374             let mut old = 0;
375             let result = VirtualProtect(ptr, mapped_len as SIZE_T, PAGE_READWRITE, &mut old);
376             if result != 0 {
377                 Ok(MmapInner {
378                     handle: None,
379                     ptr,
380                     len: len as usize,
381                     copy: false,
382                 })
383             } else {
384                 Err(io::Error::last_os_error())
385             }
386         }
387     }
388 
flush(&self, offset: usize, len: usize) -> io::Result<()>389     pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
390         self.flush_async(offset, len)?;
391 
392         if let Some(handle) = self.handle {
393             let ok = unsafe { FlushFileBuffers(handle) };
394             if ok == 0 {
395                 return Err(io::Error::last_os_error());
396             }
397         }
398 
399         Ok(())
400     }
401 
flush_async(&self, offset: usize, len: usize) -> io::Result<()>402     pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
403         if self.ptr == empty_slice_ptr() {
404             return Ok(());
405         }
406         let result = unsafe { FlushViewOfFile(self.ptr.add(offset), len as SIZE_T) };
407         if result != 0 {
408             Ok(())
409         } else {
410             Err(io::Error::last_os_error())
411         }
412     }
413 
virtual_protect(&mut self, protect: DWORD) -> io::Result<()>414     fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> {
415         if self.ptr == empty_slice_ptr() {
416             return Ok(());
417         }
418         unsafe {
419             let alignment = self.ptr as usize % allocation_granularity();
420             let ptr = self.ptr.offset(-(alignment as isize));
421             let aligned_len = self.len as SIZE_T + alignment as SIZE_T;
422 
423             let mut old = 0;
424             let result = VirtualProtect(ptr, aligned_len, protect, &mut old);
425 
426             if result != 0 {
427                 Ok(())
428             } else {
429                 Err(io::Error::last_os_error())
430             }
431         }
432     }
433 
make_read_only(&mut self) -> io::Result<()>434     pub fn make_read_only(&mut self) -> io::Result<()> {
435         self.virtual_protect(PAGE_READONLY)
436     }
437 
make_exec(&mut self) -> io::Result<()>438     pub fn make_exec(&mut self) -> io::Result<()> {
439         if self.copy {
440             self.virtual_protect(PAGE_EXECUTE_WRITECOPY)
441         } else {
442             self.virtual_protect(PAGE_EXECUTE_READ)
443         }
444     }
445 
make_mut(&mut self) -> io::Result<()>446     pub fn make_mut(&mut self) -> io::Result<()> {
447         if self.copy {
448             self.virtual_protect(PAGE_WRITECOPY)
449         } else {
450             self.virtual_protect(PAGE_READWRITE)
451         }
452     }
453 
454     #[inline]
ptr(&self) -> *const u8455     pub fn ptr(&self) -> *const u8 {
456         self.ptr as *const u8
457     }
458 
459     #[inline]
mut_ptr(&mut self) -> *mut u8460     pub fn mut_ptr(&mut self) -> *mut u8 {
461         self.ptr as *mut u8
462     }
463 
464     #[inline]
len(&self) -> usize465     pub fn len(&self) -> usize {
466         self.len
467     }
468 }
469 
470 impl Drop for MmapInner {
drop(&mut self)471     fn drop(&mut self) {
472         if self.ptr == empty_slice_ptr() {
473             return;
474         }
475         let alignment = self.ptr as usize % allocation_granularity();
476         // Any errors during unmapping/closing are ignored as the only way
477         // to report them would be through panicking which is highly discouraged
478         // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97
479         unsafe {
480             let ptr = self.ptr.offset(-(alignment as isize));
481             UnmapViewOfFile(ptr);
482 
483             if let Some(handle) = self.handle {
484                 CloseHandle(handle);
485             }
486         }
487     }
488 }
489 
490 unsafe impl Sync for MmapInner {}
491 unsafe impl Send for MmapInner {}
492 
protection_supported(handle: RawHandle, protection: DWORD) -> bool493 fn protection_supported(handle: RawHandle, protection: DWORD) -> bool {
494     unsafe {
495         let mapping = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null());
496         if mapping.is_null() {
497             return false;
498         }
499         CloseHandle(mapping);
500         true
501     }
502 }
503 
allocation_granularity() -> usize504 fn allocation_granularity() -> usize {
505     unsafe {
506         let mut info = mem::zeroed();
507         GetSystemInfo(&mut info);
508         info.dwAllocationGranularity as usize
509     }
510 }
511 
file_len(handle: RawHandle) -> io::Result<u64>512 pub fn file_len(handle: RawHandle) -> io::Result<u64> {
513     // SAFETY: We must not close the passed-in fd by dropping the File we create,
514     // we ensure this by immediately wrapping it in a ManuallyDrop.
515     unsafe {
516         let file = ManuallyDrop::new(File::from_raw_handle(handle));
517         Ok(file.metadata()?.len())
518     }
519 }
520