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