Move two windows process tests to tests/ui
This commit is contained in:
parent
550e035a59
commit
630727006f
3 changed files with 169 additions and 148 deletions
|
@ -391,154 +391,6 @@ fn test_interior_nul_in_env_value_is_error() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests that process creation flags work by debugging a process.
|
|
||||||
/// Other creation flags make it hard or impossible to detect
|
|
||||||
/// behavioral changes in the process.
|
|
||||||
#[test]
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn test_creation_flags() {
|
|
||||||
use crate::os::windows::process::CommandExt;
|
|
||||||
use crate::sys::c::{BOOL, INFINITE};
|
|
||||||
#[repr(C)]
|
|
||||||
struct DEBUG_EVENT {
|
|
||||||
pub event_code: u32,
|
|
||||||
pub process_id: u32,
|
|
||||||
pub thread_id: u32,
|
|
||||||
// This is a union in the real struct, but we don't
|
|
||||||
// need this data for the purposes of this test.
|
|
||||||
pub _junk: [u8; 164],
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "system" {
|
|
||||||
fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: u32) -> BOOL;
|
|
||||||
fn ContinueDebugEvent(dwProcessId: u32, dwThreadId: u32, dwContinueStatus: u32) -> BOOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEBUG_PROCESS: u32 = 1;
|
|
||||||
const EXIT_PROCESS_DEBUG_EVENT: u32 = 5;
|
|
||||||
const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001;
|
|
||||||
|
|
||||||
let mut child = Command::new("cmd")
|
|
||||||
.creation_flags(DEBUG_PROCESS)
|
|
||||||
.stdin(Stdio::piped())
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
|
|
||||||
let mut events = 0;
|
|
||||||
let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] };
|
|
||||||
loop {
|
|
||||||
if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
|
|
||||||
panic!("WaitForDebugEvent failed!");
|
|
||||||
}
|
|
||||||
events += 1;
|
|
||||||
|
|
||||||
if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if unsafe {
|
|
||||||
ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED)
|
|
||||||
} == 0
|
|
||||||
{
|
|
||||||
panic!("ContinueDebugEvent failed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert!(events > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests proc thread attributes by spawning a process with a custom parent process,
|
|
||||||
/// then comparing the parent process ID with the expected parent process ID.
|
|
||||||
#[test]
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn test_proc_thread_attributes() {
|
|
||||||
use crate::mem;
|
|
||||||
use crate::os::windows::io::AsRawHandle;
|
|
||||||
use crate::os::windows::process::{CommandExt, ProcThreadAttributeList};
|
|
||||||
use crate::sys::c::{BOOL, CloseHandle, HANDLE};
|
|
||||||
use crate::sys::cvt;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct PROCESSENTRY32W {
|
|
||||||
dwSize: u32,
|
|
||||||
cntUsage: u32,
|
|
||||||
th32ProcessID: u32,
|
|
||||||
th32DefaultHeapID: usize,
|
|
||||||
th32ModuleID: u32,
|
|
||||||
cntThreads: u32,
|
|
||||||
th32ParentProcessID: u32,
|
|
||||||
pcPriClassBase: i32,
|
|
||||||
dwFlags: u32,
|
|
||||||
szExeFile: [u16; 260],
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "system" {
|
|
||||||
fn CreateToolhelp32Snapshot(dwflags: u32, th32processid: u32) -> HANDLE;
|
|
||||||
fn Process32First(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL;
|
|
||||||
fn Process32Next(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
|
|
||||||
const TH32CS_SNAPPROCESS: u32 = 0x00000002;
|
|
||||||
|
|
||||||
struct ProcessDropGuard(crate::process::Child);
|
|
||||||
|
|
||||||
impl Drop for ProcessDropGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = self.0.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut parent = Command::new("cmd");
|
|
||||||
parent.stdout(Stdio::null()).stderr(Stdio::null());
|
|
||||||
|
|
||||||
let parent = ProcessDropGuard(parent.spawn().unwrap());
|
|
||||||
|
|
||||||
let mut child_cmd = Command::new("cmd");
|
|
||||||
child_cmd.stdout(Stdio::null()).stderr(Stdio::null());
|
|
||||||
|
|
||||||
let parent_process_handle = parent.0.as_raw_handle();
|
|
||||||
|
|
||||||
let mut attribute_list = ProcThreadAttributeList::build()
|
|
||||||
.attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle)
|
|
||||||
.finish()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let child = ProcessDropGuard(child_cmd.spawn_with_attributes(&mut attribute_list).unwrap());
|
|
||||||
|
|
||||||
let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
|
|
||||||
|
|
||||||
let mut process_entry = PROCESSENTRY32W {
|
|
||||||
dwSize: mem::size_of::<PROCESSENTRY32W>() as u32,
|
|
||||||
cntUsage: 0,
|
|
||||||
th32ProcessID: 0,
|
|
||||||
th32DefaultHeapID: 0,
|
|
||||||
th32ModuleID: 0,
|
|
||||||
cntThreads: 0,
|
|
||||||
th32ParentProcessID: 0,
|
|
||||||
pcPriClassBase: 0,
|
|
||||||
dwFlags: 0,
|
|
||||||
szExeFile: [0; 260],
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { cvt(Process32First(h_snapshot, &mut process_entry as *mut _)) }.unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if child.0.id() == process_entry.th32ProcessID {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
unsafe { cvt(Process32Next(h_snapshot, &mut process_entry as *mut _)) }.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { cvt(CloseHandle(h_snapshot)) }.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(parent.0.id(), process_entry.th32ParentProcessID);
|
|
||||||
|
|
||||||
drop(child)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_command_implements_send_sync() {
|
fn test_command_implements_send_sync() {
|
||||||
fn take_send_sync_type<T: Send + Sync>(_: T) {}
|
fn take_send_sync_type<T: Send + Sync>(_: T) {}
|
||||||
|
|
51
tests/ui/process/win-creation-flags.rs
Normal file
51
tests/ui/process/win-creation-flags.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Test that windows `creation_flags` extension to `Command` works.
|
||||||
|
|
||||||
|
//@ run-pass
|
||||||
|
//@ only-windows
|
||||||
|
//@ needs-subprocess
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
use std::process::{Command, exit};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if env::args().skip(1).any(|s| s == "--child") {
|
||||||
|
child();
|
||||||
|
} else {
|
||||||
|
parent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent() {
|
||||||
|
let exe = env::current_exe().unwrap();
|
||||||
|
|
||||||
|
// Use the DETACH_PROCESS to create a subprocess that isn't attached to the console.
|
||||||
|
// The subprocess's exit status will be 0 if it's detached.
|
||||||
|
let status = Command::new(&exe)
|
||||||
|
.arg("--child")
|
||||||
|
.creation_flags(DETACH_PROCESS)
|
||||||
|
.spawn()
|
||||||
|
.unwrap()
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(status.code(), Some(0));
|
||||||
|
|
||||||
|
// Try without DETACH_PROCESS to ensure this test works.
|
||||||
|
let status = Command::new(&exe).arg("--child").spawn().unwrap().wait().unwrap();
|
||||||
|
assert_eq!(status.code(), Some(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// exits with 1 if the console is attached or 0 otherwise
|
||||||
|
fn child() {
|
||||||
|
// Get the attached console's code page.
|
||||||
|
// This will fail (return 0) if no console is attached.
|
||||||
|
let has_console = GetConsoleCP() != 0;
|
||||||
|
exit(has_console as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows API definitions.
|
||||||
|
const DETACH_PROCESS: u32 = 0x00000008;
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
unsafe extern "system" {
|
||||||
|
safe fn GetConsoleCP() -> u32;
|
||||||
|
}
|
118
tests/ui/process/win-proc-thread-attributes.rs
Normal file
118
tests/ui/process/win-proc-thread-attributes.rs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
// Tests proc thread attributes by spawning a process with a custom parent process,
|
||||||
|
// then comparing the parent process ID with the expected parent process ID.
|
||||||
|
|
||||||
|
//@ run-pass
|
||||||
|
//@ only-windows
|
||||||
|
//@ needs-subprocess
|
||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
#![feature(windows_process_extensions_raw_attribute)]
|
||||||
|
|
||||||
|
use std::os::windows::io::AsRawHandle;
|
||||||
|
use std::os::windows::process::{CommandExt, ProcThreadAttributeList};
|
||||||
|
use std::process::{Child, Command};
|
||||||
|
use std::{env, mem, ptr, thread, time};
|
||||||
|
|
||||||
|
// Make a best effort to ensure child processes always exit.
|
||||||
|
struct ProcessDropGuard(Child);
|
||||||
|
impl Drop for ProcessDropGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = self.0.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if env::args().skip(1).any(|s| s == "--child") {
|
||||||
|
child();
|
||||||
|
} else {
|
||||||
|
parent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent() {
|
||||||
|
let exe = env::current_exe().unwrap();
|
||||||
|
|
||||||
|
let (fake_parent_id, child_parent_id) = {
|
||||||
|
// Create a process to be our fake parent process.
|
||||||
|
let fake_parent = Command::new(&exe).arg("--child").spawn().unwrap();
|
||||||
|
let fake_parent = ProcessDropGuard(fake_parent);
|
||||||
|
let parent_handle = fake_parent.0.as_raw_handle();
|
||||||
|
|
||||||
|
// Create another process with the parent process set to the fake.
|
||||||
|
let mut attribute_list = ProcThreadAttributeList::build()
|
||||||
|
.attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_handle)
|
||||||
|
.finish()
|
||||||
|
.unwrap();
|
||||||
|
let child =
|
||||||
|
Command::new(&exe).arg("--child").spawn_with_attributes(&mut attribute_list).unwrap();
|
||||||
|
let child = ProcessDropGuard(child);
|
||||||
|
|
||||||
|
// Return the fake's process id and the child's parent's id.
|
||||||
|
(process_info(&fake_parent.0).process_id(), process_info(&child.0).parent_id())
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(fake_parent_id, child_parent_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A process that stays running until killed.
|
||||||
|
fn child() {
|
||||||
|
// Don't wait forever if something goes wrong.
|
||||||
|
thread::sleep(time::Duration::from_secs(60));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_info(child: &Child) -> PROCESS_BASIC_INFORMATION {
|
||||||
|
unsafe {
|
||||||
|
let mut info: PROCESS_BASIC_INFORMATION = mem::zeroed();
|
||||||
|
let result = NtQueryInformationProcess(
|
||||||
|
child.as_raw_handle(),
|
||||||
|
ProcessBasicInformation,
|
||||||
|
ptr::from_mut(&mut info).cast(),
|
||||||
|
mem::size_of_val(&info).try_into().unwrap(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
);
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows API
|
||||||
|
mod winapi {
|
||||||
|
#![allow(nonstandard_style)]
|
||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
pub type HANDLE = *mut c_void;
|
||||||
|
type NTSTATUS = i32;
|
||||||
|
type PROCESSINFOCLASS = i32;
|
||||||
|
|
||||||
|
pub const ProcessBasicInformation: i32 = 0;
|
||||||
|
pub const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000;
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PROCESS_BASIC_INFORMATION {
|
||||||
|
pub ExitStatus: NTSTATUS,
|
||||||
|
pub PebBaseAddress: *mut (),
|
||||||
|
pub AffinityMask: usize,
|
||||||
|
pub BasePriority: i32,
|
||||||
|
pub UniqueProcessId: usize,
|
||||||
|
pub InheritedFromUniqueProcessId: usize,
|
||||||
|
}
|
||||||
|
impl PROCESS_BASIC_INFORMATION {
|
||||||
|
pub fn parent_id(&self) -> usize {
|
||||||
|
self.InheritedFromUniqueProcessId
|
||||||
|
}
|
||||||
|
pub fn process_id(&self) -> usize {
|
||||||
|
self.UniqueProcessId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link(name = "ntdll")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn NtQueryInformationProcess(
|
||||||
|
ProcessHandle: HANDLE,
|
||||||
|
ProcessInformationClass: PROCESSINFOCLASS,
|
||||||
|
ProcessInformation: *mut c_void,
|
||||||
|
ProcessInformationLength: u32,
|
||||||
|
ReturnLength: *mut u32,
|
||||||
|
) -> NTSTATUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
use winapi::*;
|
Loading…
Add table
Add a link
Reference in a new issue