1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Library for common Windows-specfic utilities
//!
//! TODO(b/223723424) win_util should be merged into win_sys_util or part of the
//! base.
// Do nothing on unix as win_util is windows only.
#![cfg(windows)]
// TODO: Many pub functions take `RawHandle` (which is a pointer on Windows) but are not marked
// `unsafe`.
#![allow(clippy::not_unsafe_ptr_arg_deref)]
mod large_integer;
pub use crate::large_integer::*;
mod security_attributes;
pub use crate::security_attributes::*;
mod dll_notification;
use std::ffi::CString;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::io;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::os::windows::ffi::OsStringExt;
use std::os::windows::io::RawHandle;
use std::ptr;
use std::slice;
mod keyboard;
pub use keyboard::*;
use libc::c_ulong;
use serde::Deserialize;
use serde::Serialize;
use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::FALSE;
use winapi::shared::minwindef::TRUE;
use winapi::shared::ntdef::UNICODE_STRING;
use winapi::um::handleapi::CloseHandle;
use winapi::um::handleapi::DuplicateHandle;
use winapi::um::handleapi::SetHandleInformation;
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::minwinbase::STILL_ACTIVE;
use winapi::um::processthreadsapi::GetCurrentProcess;
use winapi::um::processthreadsapi::GetExitCodeProcess;
use winapi::um::processthreadsapi::OpenProcess;
use winapi::um::processthreadsapi::ResumeThread;
use winapi::um::winbase::CreateFileMappingA;
use winapi::um::winbase::HANDLE_FLAG_INHERIT;
use winapi::um::winnt::DUPLICATE_SAME_ACCESS;
use winapi::um::winnt::HRESULT;
use winapi::um::winnt::PROCESS_DUP_HANDLE;
use winapi::um::winnt::WCHAR;
pub use crate::dll_notification::*;
pub mod dpapi;
#[macro_export]
macro_rules! syscall_bail {
($details:expr) => {
// SAFETY: Safe because GetLastError is thread safe and won't access the memory.
::anyhow::bail!("{} (Error code {})", $details, unsafe {
::winapi::um::errhandlingapi::GetLastError()
})
};
}
#[macro_export]
macro_rules! fail_if_zero {
($syscall:expr) => {
if $syscall == 0 {
return Err(io::Error::last_os_error());
}
};
}
/// Returns the lower 32 bits of a u64 as a u32 (c_ulong/DWORD)
pub fn get_low_order(number: u64) -> c_ulong {
(number & (u32::MAX as u64)) as c_ulong
}
/// Returns the upper 32 bits of a u64 as a u32 (c_ulong/DWORD)
pub fn get_high_order(number: u64) -> c_ulong {
(number >> 32) as c_ulong
}
pub fn win32_string(value: &str) -> CString {
CString::new(value).unwrap()
}
pub fn win32_wide_string(value: &str) -> Vec<u16> {
OsStr::new(value).encode_wide().chain(once(0)).collect()
}
/// Returns the length, in u16 words (*not* UTF-16 chars), of a null-terminated u16 string.
/// Safe when `wide` is non-null and points to a u16 string terminated by a null character.
unsafe fn strlen_ptr_u16(wide: *const u16) -> usize {
assert!(!wide.is_null());
for i in 0.. {
if *wide.offset(i) == 0 {
return i as usize;
}
}
unreachable!()
}
/// Converts a UTF-16 null-terminated string to an owned `String`. Any invalid code points are
/// converted to `std::char::REPLACEMENT_CHARACTER`.
///
/// # Safety
///
/// Safe when `wide` is non-null and points to a u16 string terminated by a null character.
pub unsafe fn from_ptr_win32_wide_string(wide: *const u16) -> String {
assert!(!wide.is_null());
let len = strlen_ptr_u16(wide);
let slice = slice::from_raw_parts(wide, len);
String::from_utf16_lossy(slice)
}
/// Converts a `UNICODE_STRING` into an `OsString`.
/// ## Safety
/// Safe when `unicode_string` is non-null and points to a valid
/// `UNICODE_STRING` struct.
pub fn unicode_string_to_os_string(unicode_string: &UNICODE_STRING) -> OsString {
// SAFETY:
// * Buffer is guaranteed to be properly aligned and valid for the entire length of the string.
// * The slice is only temporary, until we perform the `from_wide` conversion with `OsString`,
// so the memory referenced by the slice is not modified during that duration.
OsString::from_wide(unsafe {
slice::from_raw_parts(
unicode_string.Buffer,
unicode_string.Length as usize / std::mem::size_of::<WCHAR>(),
)
})
}
pub fn duplicate_handle_with_target_pid(hndl: RawHandle, target_pid: u32) -> io::Result<RawHandle> {
// SAFETY: Caller will guarantee `hndl` and `target_pid` are valid and won't be dropped.
unsafe {
let target_process_handle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, target_pid);
if target_process_handle.is_null() {
return Err(io::Error::last_os_error());
}
let result = duplicate_handle_with_target_handle(hndl, target_process_handle);
CloseHandle(target_process_handle);
result
}
}
pub fn duplicate_handle_from_source_process(
source_process_handle: RawHandle,
hndl: RawHandle,
target_process_handle: RawHandle,
) -> io::Result<RawHandle> {
// SAFETY:
// 1. We are checking the return code
// 2. new_handle_ptr points to a valid location on the stack
// 3. Caller guarantees hndl is a real valid handle.
unsafe {
let mut new_handle: RawHandle = ptr::null_mut();
let success_flag = DuplicateHandle(
/* hSourceProcessHandle= */ source_process_handle,
/* hSourceHandle= */ hndl,
/* hTargetProcessHandle= */ target_process_handle,
/* lpTargetHandle= */ &mut new_handle,
/* dwDesiredAccess= */ 0,
/* bInheritHandle= */ TRUE,
/* dwOptions= */ DUPLICATE_SAME_ACCESS,
);
if success_flag == FALSE {
Err(io::Error::last_os_error())
} else {
Ok(new_handle)
}
}
}
fn duplicate_handle_with_target_handle(
hndl: RawHandle,
target_process_handle: RawHandle,
) -> io::Result<RawHandle> {
duplicate_handle_from_source_process(
// SAFETY: `GetCurrentProcess` just gets the current process handle.
unsafe { GetCurrentProcess() },
hndl,
target_process_handle,
)
}
pub fn duplicate_handle(hndl: RawHandle) -> io::Result<RawHandle> {
// SAFETY: `GetCurrentProcess` just gets the current process handle.
duplicate_handle_with_target_handle(hndl, unsafe { GetCurrentProcess() })
}
/// Sets whether a handle is inheritable. Note that this only works on some types of handles,
/// such as files, pipes, etc. See
/// <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-sethandleinformation#parameters>
/// for further details.
pub fn set_handle_inheritance(hndl: RawHandle, inheritable: bool) -> io::Result<()> {
// SAFETY: Even if hndl is invalid, no unsafe memory access will result.
let res = unsafe {
SetHandleInformation(
hndl,
HANDLE_FLAG_INHERIT,
if inheritable { HANDLE_FLAG_INHERIT } else { 0 },
)
};
if res == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
/// Rusty version of CreateFileMappingA.
///
/// # Safety
/// If provided, the caller must ensure hndl is valid.
pub unsafe fn create_file_mapping(
handle: Option<RawHandle>,
size: u64,
protection: DWORD,
name: Option<&str>,
) -> io::Result<RawHandle> {
let name_cstr = name.map(|s| CString::new(s).unwrap());
let name = name_cstr.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut());
// Safe because:
// 1. The caller guarantees handle is valid (if provided).
// 2. The C string is guaranteed valid.
// 3. We check the results of the call.
let mapping_handle = CreateFileMappingA(
match handle {
Some(h) => h,
None => INVALID_HANDLE_VALUE,
},
SecurityAttributes::new_with_security_descriptor(
SelfRelativeSecurityDescriptor::get_singleton(),
/* inherit= */ true,
)
.as_mut(),
protection,
get_high_order(size),
get_low_order(size),
name,
);
if mapping_handle.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(mapping_handle)
}
}
#[derive(PartialEq, Eq)]
pub enum ThreadState {
// The specified thread was not suspended.
NotSuspended,
// The specified thread was suspended, but was restarted.
Restarted,
// The specified thread is still suspended.
StillSuspended,
}
/// Decrements a thread's suspend count. When the suspend count reaches 0, the
/// thread is resumed. Returned `ThreadState` indicates whether the thread was
/// resumed.
pub fn resume_thread(handle: RawHandle) -> io::Result<ThreadState> {
// SAFETY: Even an invalid handle should cause no adverse effects.
match unsafe { ResumeThread(handle) } {
u32::MAX => Err(io::Error::last_os_error()),
0 => Ok(ThreadState::NotSuspended),
1 => Ok(ThreadState::Restarted),
_ => Ok(ThreadState::StillSuspended),
}
}
/// Retrieves the termination status of the specified process.
pub fn get_exit_code_process(handle: RawHandle) -> io::Result<Option<DWORD>> {
let mut exit_code: DWORD = 0;
// SAFETY: Even an invalid handle should cause no adverse effects.
match unsafe { GetExitCodeProcess(handle, &mut exit_code) } {
0 => Err(io::Error::last_os_error()),
_ => {
if exit_code == STILL_ACTIVE {
Ok(None)
} else {
Ok(Some(exit_code))
}
}
}
}
pub type HResult<T> = Result<T, HRESULT>;
/// Each type of process should have its own type here. This affects both exit
/// handling and sandboxing policy.
///
/// WARNING: do NOT change the values items in this enum. The enum value is used in our exit codes,
/// and relied upon by metrics analysis. The max value for this enum is 0x1F = 31 as it is
/// restricted to five bits per `crate::crosvm::sys::windows::exit::to_process_type_error`.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize, enumn::N)]
#[repr(u8)]
pub enum ProcessType {
UnknownType = 0,
Block = 1,
Main = 2,
Metrics = 3,
Net = 4,
Slirp = 5,
Gpu = 6,
Snd = 7,
Broker = 8,
Spu = 9,
}
/// State of a crosvm child process.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub enum ProcessState {
UnknownState,
/// Process is running normally.
Healthy,
/// Process died unexpectedly - it is either killed, crashed or something else.
Died,
/// Process exited on request or gracefully.
Exited,
}
/// Priority of a crosvm child process.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub enum ProcessPriority {
UnknwonPriority,
/// Crosvm critical process. In absence of this process crosvm cannot function normally.
Critical,
/// Non-critical process - the process is safe to restart. Crosvm/guest may continue to
/// function normally when such process dies.
NonCritical,
}
/// Information about crosvm child process.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub struct ProcessInfo {
pub id: u64,
pub ptype: ProcessType,
pub priority: ProcessPriority,
pub state: ProcessState,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn high_low_order_utilities() {
let some_number: u64 = 0xA3200500FFB40123;
let high_order: u64 = get_high_order(some_number).into();
let low_order: u64 = get_low_order(some_number).into();
assert_eq!(some_number, (high_order << 32) + low_order);
}
#[test]
fn strlen() {
let u16s = [0];
// SAFETY: u16s is a valid wide string
assert_eq!(unsafe { strlen_ptr_u16(u16s.as_ptr()) }, 0);
let u16s = [
0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, 0,
];
// SAFETY: u16s is a valid wide string
assert_eq!(unsafe { strlen_ptr_u16(u16s.as_ptr()) }, 9);
}
#[test]
fn from_win32_wide_string() {
let u16s = [0];
// SAFETY: u16s is a valid wide string
assert_eq!(unsafe { from_ptr_win32_wide_string(u16s.as_ptr()) }, "");
let u16s = [
0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, 0,
];
assert_eq!(
// SAFETY: u16s is a valid wide string
unsafe { from_ptr_win32_wide_string(u16s.as_ptr()) },
"𝄞mus�ic�"
);
}
}