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