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