| // 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::*; |