• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Library for common Windows-specfic utilities
6 //!
7 //! TODO(b/223723424) win_util should be merged into win_sys_util or part of the
8 //! base.
9 
10 // Do nothing on unix as win_util is windows only.
11 #![cfg(windows)]
12 
13 mod large_integer;
14 pub use crate::large_integer::*;
15 
16 mod security_attributes;
17 pub use crate::security_attributes::*;
18 
19 use libc::c_ulong;
20 use std::ffi::{CString, OsStr};
21 use std::iter::once;
22 use std::mem::MaybeUninit;
23 use std::os::windows::ffi::OsStrExt;
24 use std::os::windows::io::RawHandle;
25 use std::slice;
26 use std::sync::Once;
27 use std::{io, ptr};
28 use winapi::shared::minwindef::{DWORD, FALSE, TRUE};
29 use winapi::um::handleapi::{
30     CloseHandle, DuplicateHandle, SetHandleInformation, INVALID_HANDLE_VALUE,
31 };
32 use winapi::um::minwinbase::STILL_ACTIVE;
33 use winapi::um::processthreadsapi::{
34     GetCurrentProcess, GetExitCodeProcess, OpenProcess, ResumeThread,
35 };
36 use winapi::um::sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO};
37 use winapi::um::winbase::{CreateFileMappingA, HANDLE_FLAG_INHERIT};
38 use winapi::um::winnt::{DUPLICATE_SAME_ACCESS, HRESULT, PROCESS_DUP_HANDLE};
39 
40 #[macro_export]
41 macro_rules! syscall_bail {
42     ($details:expr) => {
43         ::anyhow::bail!(
44             "{} (Error code {})",
45             $details,
46             ::winapi::um::errhandlingapi::GetLastError()
47         )
48     };
49 }
50 
51 /// Returns the lower 32 bits of a u64 as a u32 (c_ulong/DWORD)
get_low_order(number: u64) -> c_ulong52 pub fn get_low_order(number: u64) -> c_ulong {
53     (number & (u32::max_value() as u64)) as c_ulong
54 }
55 
56 /// Returns the upper 32 bits of a u64 as a u32 (c_ulong/DWORD)
get_high_order(number: u64) -> c_ulong57 pub fn get_high_order(number: u64) -> c_ulong {
58     (number >> 32) as c_ulong
59 }
60 
61 static INIT_NATIVE_SYSTEM_INFO: Once = Once::new();
62 static mut NATIVE_SYSTEM_INFO: MaybeUninit<SYSTEM_INFO> = MaybeUninit::uninit();
63 
pagesize() -> usize64 pub fn pagesize() -> usize {
65     get_native_system_info().dwPageSize as usize
66 }
67 
allocation_granularity() -> u6468 pub fn allocation_granularity() -> u64 {
69     get_native_system_info().dwAllocationGranularity as u64
70 }
71 
number_of_processors() -> usize72 pub fn number_of_processors() -> usize {
73     get_native_system_info().dwNumberOfProcessors as usize
74 }
75 
get_native_system_info() -> SYSTEM_INFO76 fn get_native_system_info() -> SYSTEM_INFO {
77     INIT_NATIVE_SYSTEM_INFO.call_once(|| unsafe {
78         // Safe because this is a universally available call on modern Windows systems.
79         GetNativeSystemInfo(NATIVE_SYSTEM_INFO.as_mut_ptr());
80     });
81     // Safe because it is guaranteed to be initialized by GetNativeSystemInfo above.
82     unsafe { NATIVE_SYSTEM_INFO.assume_init() }
83 }
84 
win32_string(value: &str) -> CString85 pub fn win32_string(value: &str) -> CString {
86     CString::new(value).unwrap()
87 }
88 
win32_wide_string(value: &str) -> Vec<u16>89 pub fn win32_wide_string(value: &str) -> Vec<u16> {
90     OsStr::new(value).encode_wide().chain(once(0)).collect()
91 }
92 
93 /// Returns the length, in u16 words (*not* UTF-16 chars), of a null-terminated u16 string.
94 /// Safe when `wide` is non-null and points to a u16 string terminated by a null character.
strlen_ptr_u16(wide: *const u16) -> usize95 unsafe fn strlen_ptr_u16(wide: *const u16) -> usize {
96     assert!(!wide.is_null());
97     for i in 0.. {
98         if *wide.offset(i) == 0 {
99             return i as usize;
100         }
101     }
102     unreachable!()
103 }
104 
105 /// Converts a UTF-16 null-terminated string to an owned `String`.  Any invalid code points are
106 /// converted to `std::char::REPLACEMENT_CHARACTER`.
107 /// Safe when `wide` is non-null and points to a u16 string terminated by a null character.
from_ptr_win32_wide_string(wide: *const u16) -> String108 pub unsafe fn from_ptr_win32_wide_string(wide: *const u16) -> String {
109     assert!(!wide.is_null());
110     let len = strlen_ptr_u16(wide);
111     let slice = slice::from_raw_parts(wide, len);
112     String::from_utf16_lossy(slice)
113 }
114 
duplicate_handle_with_target_pid(hndl: RawHandle, target_pid: u32) -> io::Result<RawHandle>115 pub fn duplicate_handle_with_target_pid(hndl: RawHandle, target_pid: u32) -> io::Result<RawHandle> {
116     // Safe because caller will guarentee `hndl` and `target_pid` are valid and won't be dropped.
117     unsafe {
118         let target_process_handle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, target_pid);
119         if target_process_handle.is_null() {
120             return Err(io::Error::last_os_error());
121         }
122         let result = duplicate_handle_with_target_handle(hndl, target_process_handle);
123         CloseHandle(target_process_handle);
124         result
125     }
126 }
127 
duplicate_handle_from_source_process( source_process_handle: RawHandle, hndl: RawHandle, target_process_handle: RawHandle, ) -> io::Result<RawHandle>128 pub fn duplicate_handle_from_source_process(
129     source_process_handle: RawHandle,
130     hndl: RawHandle,
131     target_process_handle: RawHandle,
132 ) -> io::Result<RawHandle> {
133     // Safe because:
134     // 1. We are checking the return code
135     // 2. new_handle_ptr points to a valid location on the stack
136     // 3. Caller guarantees hndl is a real valid handle.
137     unsafe {
138         let mut new_handle: RawHandle = ptr::null_mut();
139         let success_flag = DuplicateHandle(
140             /* hSourceProcessHandle= */ source_process_handle,
141             /* hSourceHandle= */ hndl,
142             /* hTargetProcessHandle= */ target_process_handle,
143             /* lpTargetHandle= */ &mut new_handle,
144             /* dwDesiredAccess= */ 0,
145             /* bInheritHandle= */ TRUE,
146             /* dwOptions= */ DUPLICATE_SAME_ACCESS,
147         );
148 
149         if success_flag == FALSE {
150             Err(io::Error::last_os_error())
151         } else {
152             Ok(new_handle)
153         }
154     }
155 }
156 
duplicate_handle_with_target_handle( hndl: RawHandle, target_process_handle: RawHandle, ) -> io::Result<RawHandle>157 fn duplicate_handle_with_target_handle(
158     hndl: RawHandle,
159     target_process_handle: RawHandle,
160 ) -> io::Result<RawHandle> {
161     // Safe because `GetCurrentProcess` just gets the current process handle.
162     duplicate_handle_from_source_process(
163         unsafe { GetCurrentProcess() },
164         hndl,
165         target_process_handle,
166     )
167 }
168 
duplicate_handle(hndl: RawHandle) -> io::Result<RawHandle>169 pub fn duplicate_handle(hndl: RawHandle) -> io::Result<RawHandle> {
170     // Safe because `GetCurrentProcess` just gets the current process handle.
171     duplicate_handle_with_target_handle(hndl, unsafe { GetCurrentProcess() })
172 }
173 
174 /// Sets whether a handle is inheritable. Note that this only works on some types of handles,
175 /// such as files, pipes, etc. See
176 /// https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-sethandleinformation#parameters
177 /// for further details.
set_handle_inheritance(hndl: RawHandle, inheritable: bool) -> io::Result<()>178 pub fn set_handle_inheritance(hndl: RawHandle, inheritable: bool) -> io::Result<()> {
179     // Safe because even if hndl is invalid, no unsafe memory access will result.
180     let res = unsafe {
181         SetHandleInformation(
182             hndl,
183             HANDLE_FLAG_INHERIT,
184             if inheritable { HANDLE_FLAG_INHERIT } else { 0 },
185         )
186     };
187     if res == 0 {
188         Err(io::Error::last_os_error())
189     } else {
190         Ok(())
191     }
192 }
193 
194 /// Rusty version of CreateFileMappingA.
195 ///
196 /// # Safety
197 /// If provided, the caller must ensure hndl is valid.
create_file_mapping( handle: Option<RawHandle>, size: u64, protection: DWORD, name: Option<&str>, ) -> io::Result<RawHandle>198 pub unsafe fn create_file_mapping(
199     handle: Option<RawHandle>,
200     size: u64,
201     protection: DWORD,
202     name: Option<&str>,
203 ) -> io::Result<RawHandle> {
204     let name_cstr = name.map(|s| CString::new(s).unwrap());
205     let name = name_cstr.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut());
206 
207     // Safe because:
208     // 1. The caller guarantees handle is valid (if provided).
209     // 2. The C string is guaranteed valid.
210     // 3. We check the results of the call.
211     let mapping_handle = CreateFileMappingA(
212         match handle {
213             Some(h) => h,
214             None => INVALID_HANDLE_VALUE,
215         },
216         SecurityAttributes::new_with_security_descriptor(
217             SelfRelativeSecurityDescriptor::get_singleton(),
218             /* inherit= */ true,
219         )
220         .as_mut(),
221         protection,
222         get_high_order(size),
223         get_low_order(size),
224         name,
225     );
226 
227     if mapping_handle.is_null() {
228         Err(io::Error::last_os_error())
229     } else {
230         Ok(mapping_handle)
231     }
232 }
233 
234 #[derive(PartialEq)]
235 pub enum ThreadState {
236     // The specified thread was not suspended.
237     NotSuspended,
238     // The specified thread was suspended, but was restarted.
239     Restarted,
240     // The specified thread is still suspended.
241     StillSuspended,
242 }
243 
244 /// Decrements a thread's suspend count. When the suspend count reaches 0, the
245 /// thread is resumed. Returned `ThreadState` indicates whether the thread was
246 /// resumed.
resume_thread(handle: RawHandle) -> io::Result<ThreadState>247 pub fn resume_thread(handle: RawHandle) -> io::Result<ThreadState> {
248     // Safe as even an invalid handle should cause no adverse effects.
249     match unsafe { ResumeThread(handle) } {
250         u32::MAX => Err(io::Error::last_os_error()),
251         0 => Ok(ThreadState::NotSuspended),
252         1 => Ok(ThreadState::Restarted),
253         _ => Ok(ThreadState::StillSuspended),
254     }
255 }
256 
257 /// Retrieves the termination status of the specified process.
get_exit_code_process(handle: RawHandle) -> io::Result<Option<DWORD>>258 pub fn get_exit_code_process(handle: RawHandle) -> io::Result<Option<DWORD>> {
259     let mut exit_code: DWORD = 0;
260     // Safe as even an invalid handle should cause no adverse effects.
261     match unsafe { GetExitCodeProcess(handle, &mut exit_code) } {
262         0 => Err(io::Error::last_os_error()),
263         _ => {
264             if exit_code == STILL_ACTIVE {
265                 Ok(None)
266             } else {
267                 Ok(Some(exit_code))
268             }
269         }
270     }
271 }
272 
273 pub type HResult<T> = Result<T, HRESULT>;
274 
275 // windows-rs bindings
276 #[cfg(target_env = "msvc")]
277 mod bindings {
278     ::windows::include_bindings!();
279 }
280 #[cfg(target_env = "msvc")]
281 pub use bindings::Windows::Win32::Globalization::ImmDisableIME;
282 
283 #[cfg(test)]
284 mod tests {
285     use super::*;
286 
287     #[test]
high_low_order_utilities()288     fn high_low_order_utilities() {
289         let some_number: u64 = 0xA3200500FFB40123;
290         let high_order: u64 = get_high_order(some_number).into();
291         let low_order: u64 = get_low_order(some_number).into();
292         assert_eq!(some_number, (high_order << 32) + low_order);
293     }
294 
295     #[test]
strlen()296     fn strlen() {
297         let u16s = [0];
298         assert_eq!(unsafe { strlen_ptr_u16((&u16s).as_ptr()) }, 0);
299         let u16s = [
300             0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, 0,
301         ];
302         assert_eq!(unsafe { strlen_ptr_u16((&u16s).as_ptr()) }, 9);
303     }
304 
305     #[test]
from_win32_wide_string()306     fn from_win32_wide_string() {
307         let u16s = [0];
308         assert_eq!(unsafe { from_ptr_win32_wide_string((&u16s).as_ptr()) }, "");
309         let u16s = [
310             0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, 0,
311         ];
312         assert_eq!(
313             unsafe { from_ptr_win32_wide_string((&u16s).as_ptr()) },
314             "��mus�ic�"
315         );
316     }
317 }
318