• 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, _huge: Option<u8>, ) -> io::Result<MmapInner>346     pub fn map_anon(
347         len: usize,
348         _stack: bool,
349         _populate: bool,
350         _huge: Option<u8>,
351     ) -> io::Result<MmapInner> {
352         // Ensure a non-zero length for the underlying mapping
353         let mapped_len = len.max(1);
354         unsafe {
355             // Create a mapping and view with maximum access permissions, then use `VirtualProtect`
356             // to set the actual `Protection`. This way, we can set more permissive protection later
357             // on.
358             // Also see https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537.aspx
359 
360             let mapping = CreateFileMappingW(
361                 INVALID_HANDLE_VALUE,
362                 ptr::null_mut(),
363                 PAGE_EXECUTE_READWRITE,
364                 (mapped_len >> 16 >> 16) as DWORD,
365                 (mapped_len & 0xffffffff) as DWORD,
366                 ptr::null(),
367             );
368             if mapping.is_null() {
369                 return Err(io::Error::last_os_error());
370             }
371             let access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
372             let ptr = MapViewOfFile(mapping, access, 0, 0, mapped_len as SIZE_T);
373             CloseHandle(mapping);
374 
375             if ptr.is_null() {
376                 return Err(io::Error::last_os_error());
377             }
378 
379             let mut old = 0;
380             let result = VirtualProtect(ptr, mapped_len as SIZE_T, PAGE_READWRITE, &mut old);
381             if result != 0 {
382                 Ok(MmapInner {
383                     handle: None,
384                     ptr,
385                     len: len as usize,
386                     copy: false,
387                 })
388             } else {
389                 Err(io::Error::last_os_error())
390             }
391         }
392     }
393 
flush(&self, offset: usize, len: usize) -> io::Result<()>394     pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
395         self.flush_async(offset, len)?;
396 
397         if let Some(handle) = self.handle {
398             let ok = unsafe { FlushFileBuffers(handle) };
399             if ok == 0 {
400                 return Err(io::Error::last_os_error());
401             }
402         }
403 
404         Ok(())
405     }
406 
flush_async(&self, offset: usize, len: usize) -> io::Result<()>407     pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
408         if self.ptr == empty_slice_ptr() {
409             return Ok(());
410         }
411         let result = unsafe { FlushViewOfFile(self.ptr.add(offset), len as SIZE_T) };
412         if result != 0 {
413             Ok(())
414         } else {
415             Err(io::Error::last_os_error())
416         }
417     }
418 
virtual_protect(&mut self, protect: DWORD) -> io::Result<()>419     fn virtual_protect(&mut self, protect: DWORD) -> io::Result<()> {
420         if self.ptr == empty_slice_ptr() {
421             return Ok(());
422         }
423         unsafe {
424             let alignment = self.ptr as usize % allocation_granularity();
425             let ptr = self.ptr.offset(-(alignment as isize));
426             let aligned_len = self.len as SIZE_T + alignment as SIZE_T;
427 
428             let mut old = 0;
429             let result = VirtualProtect(ptr, aligned_len, protect, &mut old);
430 
431             if result != 0 {
432                 Ok(())
433             } else {
434                 Err(io::Error::last_os_error())
435             }
436         }
437     }
438 
make_read_only(&mut self) -> io::Result<()>439     pub fn make_read_only(&mut self) -> io::Result<()> {
440         self.virtual_protect(PAGE_READONLY)
441     }
442 
make_exec(&mut self) -> io::Result<()>443     pub fn make_exec(&mut self) -> io::Result<()> {
444         if self.copy {
445             self.virtual_protect(PAGE_EXECUTE_WRITECOPY)
446         } else {
447             self.virtual_protect(PAGE_EXECUTE_READ)
448         }
449     }
450 
make_mut(&mut self) -> io::Result<()>451     pub fn make_mut(&mut self) -> io::Result<()> {
452         if self.copy {
453             self.virtual_protect(PAGE_WRITECOPY)
454         } else {
455             self.virtual_protect(PAGE_READWRITE)
456         }
457     }
458 
459     #[inline]
ptr(&self) -> *const u8460     pub fn ptr(&self) -> *const u8 {
461         self.ptr as *const u8
462     }
463 
464     #[inline]
mut_ptr(&mut self) -> *mut u8465     pub fn mut_ptr(&mut self) -> *mut u8 {
466         self.ptr as *mut u8
467     }
468 
469     #[inline]
len(&self) -> usize470     pub fn len(&self) -> usize {
471         self.len
472     }
473 }
474 
475 impl Drop for MmapInner {
drop(&mut self)476     fn drop(&mut self) {
477         if self.ptr == empty_slice_ptr() {
478             return;
479         }
480         let alignment = self.ptr as usize % allocation_granularity();
481         // Any errors during unmapping/closing are ignored as the only way
482         // to report them would be through panicking which is highly discouraged
483         // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97
484         unsafe {
485             let ptr = self.ptr.offset(-(alignment as isize));
486             UnmapViewOfFile(ptr);
487 
488             if let Some(handle) = self.handle {
489                 CloseHandle(handle);
490             }
491         }
492     }
493 }
494 
495 unsafe impl Sync for MmapInner {}
496 unsafe impl Send for MmapInner {}
497 
protection_supported(handle: RawHandle, protection: DWORD) -> bool498 fn protection_supported(handle: RawHandle, protection: DWORD) -> bool {
499     unsafe {
500         let mapping = CreateFileMappingW(handle, ptr::null_mut(), protection, 0, 0, ptr::null());
501         if mapping.is_null() {
502             return false;
503         }
504         CloseHandle(mapping);
505         true
506     }
507 }
508 
allocation_granularity() -> usize509 fn allocation_granularity() -> usize {
510     unsafe {
511         let mut info = mem::zeroed();
512         GetSystemInfo(&mut info);
513         info.dwAllocationGranularity as usize
514     }
515 }
516 
file_len(handle: RawHandle) -> io::Result<u64>517 pub fn file_len(handle: RawHandle) -> io::Result<u64> {
518     // SAFETY: We must not close the passed-in fd by dropping the File we create,
519     // we ensure this by immediately wrapping it in a ManuallyDrop.
520     unsafe {
521         let file = ManuallyDrop::new(File::from_raw_handle(handle));
522         Ok(file.metadata()?.len())
523     }
524 }
525