Skip to content

Commit 0475e2f

Browse files
committed
add windows ext method for proc thread attributes
Added doc comments, don't take too many attrs kill the cmds in test
1 parent 8876ca3 commit 0475e2f

File tree

4 files changed

+190
-28
lines changed

4 files changed

+190
-28
lines changed

library/std/src/os/windows/process.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
#![stable(feature = "process_extensions", since = "1.2.0")]
66

7-
use crate::ffi::OsStr;
7+
use crate::ffi::{c_void, OsStr};
88
use crate::os::windows::io::{
99
AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
1010
};
@@ -161,6 +161,23 @@ pub trait CommandExt: Sealed {
161161
/// `CommandLineToArgvW` escaping rules.
162162
#[unstable(feature = "windows_process_extensions_raw_arg", issue = "29494")]
163163
fn raw_arg<S: AsRef<OsStr>>(&mut self, text_to_append_as_is: S) -> &mut process::Command;
164+
165+
/// Attach the specified proc thread attribute. Multiple attributes may be attached.
166+
///
167+
/// # Safety
168+
///
169+
/// - The data pointed to by `value` pointer must not be moved or aliased for the entire lifetime of
170+
/// the `Command`.
171+
/// - The data pointed to by `value` pointer must be initialized.
172+
/// - `size` must not exceed the size of the object pointed to by the `value` pointer.
173+
/// - It must be safe to read the data pointed to by `value` from another thread.
174+
#[unstable(feature = "windows_process_extensions_proc_thread_attributes", issue = "none")]
175+
unsafe fn process_thread_attribute(
176+
&mut self,
177+
attribute: usize,
178+
value: *mut c_void,
179+
size: usize,
180+
) -> &mut process::Command;
164181
}
165182

166183
#[stable(feature = "windows_process_extensions", since = "1.16.0")]
@@ -179,4 +196,13 @@ impl CommandExt for process::Command {
179196
self.as_inner_mut().raw_arg(raw_text.as_ref());
180197
self
181198
}
199+
unsafe fn process_thread_attribute(
200+
&mut self,
201+
attribute: usize,
202+
value: *mut c_void,
203+
size: usize,
204+
) -> &mut process::Command {
205+
self.as_inner_mut().process_thread_attribute(attribute, value, size);
206+
self
207+
}
182208
}

library/std/src/process/tests.rs

+36
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,42 @@ fn test_creation_flags() {
405405
}
406406
assert!(events > 0);
407407
}
408+
/// Tests proc thread attributes by spawning a process with a custom parent process,
409+
/// then comparing the parent process ID with the expected parent process ID.
410+
#[test]
411+
#[cfg(windows)]
412+
fn test_proc_thread_attributes() {
413+
use crate::os::windows::io::AsRawHandle;
414+
use crate::os::windows::process::CommandExt;
415+
use crate::sys::c::{DWORD_PTR, HANDLE};
416+
const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: DWORD_PTR = 0x00020000;
417+
let mut parent = Command::new("cmd.exe").spawn().unwrap();
418+
let mut parent_handle: HANDLE = parent.as_raw_handle();
419+
420+
let mut child_cmd = Command::new("cmd.exe");
421+
unsafe {
422+
child_cmd.process_thread_attribute(
423+
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
424+
&mut parent_handle as *mut _ as *mut _,
425+
crate::mem::size_of::<HANDLE>(),
426+
);
427+
}
428+
let mut child = child_cmd.spawn().unwrap();
429+
430+
let wmic = format!("wmic process where processid={} get parentprocessid", child.id());
431+
assert_eq!(
432+
parent.id(),
433+
String::from_utf8(Command::new("cmd.exe").args(["/c", &wmic]).output().unwrap().stdout)
434+
.unwrap()
435+
.lines()
436+
.skip(1)
437+
.map(|line| line.trim().parse().unwrap())
438+
.next()
439+
.unwrap()
440+
);
441+
child.kill().unwrap();
442+
parent.kill().unwrap();
443+
}
408444

409445
#[test]
410446
fn test_command_implements_send_sync() {

library/std/src/sys/windows/c.rs

+25
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub type LPCWSTR = *const WCHAR;
5151
pub type LPDWORD = *mut DWORD;
5252
pub type LPHANDLE = *mut HANDLE;
5353
pub type LPOVERLAPPED = *mut OVERLAPPED;
54+
pub type LPPROC_THREAD_ATTRIBUTE_LIST = *mut c_void;
5455
pub type LPPROCESS_INFORMATION = *mut PROCESS_INFORMATION;
5556
pub type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES;
5657
pub type LPSTARTUPINFO = *mut STARTUPINFO;
@@ -68,6 +69,7 @@ pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = *mut c_void;
6869

6970
pub type PCONDITION_VARIABLE = *mut CONDITION_VARIABLE;
7071
pub type PLARGE_INTEGER = *mut c_longlong;
72+
pub type PSIZE_T = *mut usize;
7173
pub type PSRWLOCK = *mut SRWLOCK;
7274

7375
pub type SOCKET = crate::os::windows::raw::SOCKET;
@@ -196,6 +198,7 @@ pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: ptr::null_mut() };
196198
pub const DETACHED_PROCESS: DWORD = 0x00000008;
197199
pub const CREATE_NEW_PROCESS_GROUP: DWORD = 0x00000200;
198200
pub const CREATE_UNICODE_ENVIRONMENT: DWORD = 0x00000400;
201+
pub const EXTENDED_STARTUPINFO_PRESENT: DWORD = 0x00080000;
199202
pub const STARTF_USESTDHANDLES: DWORD = 0x00000100;
200203

201204
pub const AF_INET: c_int = 2;
@@ -587,6 +590,12 @@ pub struct STARTUPINFO {
587590
pub hStdError: HANDLE,
588591
}
589592

593+
#[repr(C)]
594+
pub struct STARTUPINFOEX {
595+
pub StartupInfo: STARTUPINFO,
596+
pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST,
597+
}
598+
590599
#[repr(C)]
591600
pub struct SOCKADDR {
592601
pub sa_family: ADDRESS_FAMILY,
@@ -1078,6 +1087,22 @@ extern "system" {
10781087
lpFilePart: *mut LPWSTR,
10791088
) -> DWORD;
10801089
pub fn GetFileAttributesW(lpFileName: LPCWSTR) -> DWORD;
1090+
pub fn InitializeProcThreadAttributeList(
1091+
lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST,
1092+
dwAttributeCount: DWORD,
1093+
dwFlags: DWORD,
1094+
lpSize: PSIZE_T,
1095+
) -> BOOL;
1096+
pub fn UpdateProcThreadAttribute(
1097+
lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST,
1098+
dwFlags: DWORD,
1099+
Attribute: DWORD_PTR,
1100+
lpValue: LPVOID,
1101+
cbSize: SIZE_T,
1102+
lpPreviousValue: LPVOID,
1103+
lpReturnSize: PSIZE_T,
1104+
) -> BOOL;
1105+
pub fn DeleteProcThreadAttributeList(lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST);
10811106
}
10821107

10831108
#[link(name = "ws2_32")]

library/std/src/sys/windows/process.rs

+102-27
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::num::NonZeroI32;
1616
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
1717
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle};
1818
use crate::path::{Path, PathBuf};
19+
use crate::pin::Pin;
1920
use crate::ptr;
2021
use crate::sys::c;
2122
use crate::sys::c::NonZeroDWORD;
@@ -166,6 +167,7 @@ pub struct Command {
166167
stdout: Option<Stdio>,
167168
stderr: Option<Stdio>,
168169
force_quotes_enabled: bool,
170+
proc_thread_attributes: BTreeMap<usize, ProcThreadAttributeValue>,
169171
}
170172

171173
pub enum Stdio {
@@ -202,6 +204,7 @@ impl Command {
202204
stdout: None,
203205
stderr: None,
204206
force_quotes_enabled: false,
207+
proc_thread_attributes: Default::default(),
205208
}
206209
}
207210

@@ -251,6 +254,9 @@ impl Command {
251254
pub fn get_current_dir(&self) -> Option<&Path> {
252255
self.cwd.as_ref().map(|cwd| Path::new(cwd))
253256
}
257+
pub fn process_thread_attribute(&mut self, attribute: usize, ptr: *mut c_void, size: usize) {
258+
self.proc_thread_attributes.insert(attribute, ProcThreadAttributeValue { ptr, size });
259+
}
254260

255261
pub fn spawn(
256262
&mut self,
@@ -259,9 +265,9 @@ impl Command {
259265
) -> io::Result<(Process, StdioPipes)> {
260266
let maybe_env = self.env.capture_if_changed();
261267

262-
let mut si = zeroed_startupinfo();
263-
si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
264-
si.dwFlags = c::STARTF_USESTDHANDLES;
268+
let mut si = zeroed_startupinfo_ex();
269+
si.StartupInfo.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
270+
si.StartupInfo.dwFlags = c::STARTF_USESTDHANDLES;
265271

266272
let child_paths = if let Some(env) = maybe_env.as_ref() {
267273
env.get(&EnvKey::new("PATH")).map(|s| s.as_os_str())
@@ -305,9 +311,24 @@ impl Command {
305311
let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?;
306312
let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?;
307313
let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
308-
si.hStdInput = stdin.as_raw_handle();
309-
si.hStdOutput = stdout.as_raw_handle();
310-
si.hStdError = stderr.as_raw_handle();
314+
si.StartupInfo.hStdInput = stdin.as_raw_handle();
315+
si.StartupInfo.hStdOutput = stdout.as_raw_handle();
316+
si.StartupInfo.hStdError = stderr.as_raw_handle();
317+
318+
let mut attributes = if !self.proc_thread_attributes.is_empty() {
319+
Some(make_proc_thread_attributes(&self.proc_thread_attributes)?)
320+
} else {
321+
None
322+
};
323+
si.lpAttributeList = attributes.as_mut().map_or(ptr::null_mut(), |buf| {
324+
// Indicate that lpAttributeList is present by setting
325+
// EXTENDED_STARTUPINFO_PRESENT and adjusting the size
326+
// value of the struct.
327+
flags |= c::EXTENDED_STARTUPINFO_PRESENT;
328+
si.StartupInfo.cb = mem::size_of::<c::STARTUPINFOEX>() as u32;
329+
330+
buf.0.as_mut_ptr().cast()
331+
});
311332

312333
let program = to_u16s(&program)?;
313334
unsafe {
@@ -320,7 +341,7 @@ impl Command {
320341
flags,
321342
envp,
322343
dirp,
323-
&mut si,
344+
&mut si as *mut _ as *mut _,
324345
&mut pi,
325346
))
326347
}?;
@@ -687,26 +708,29 @@ impl From<u8> for ExitCode {
687708
}
688709
}
689710

690-
fn zeroed_startupinfo() -> c::STARTUPINFO {
691-
c::STARTUPINFO {
692-
cb: 0,
693-
lpReserved: ptr::null_mut(),
694-
lpDesktop: ptr::null_mut(),
695-
lpTitle: ptr::null_mut(),
696-
dwX: 0,
697-
dwY: 0,
698-
dwXSize: 0,
699-
dwYSize: 0,
700-
dwXCountChars: 0,
701-
dwYCountCharts: 0,
702-
dwFillAttribute: 0,
703-
dwFlags: 0,
704-
wShowWindow: 0,
705-
cbReserved2: 0,
706-
lpReserved2: ptr::null_mut(),
707-
hStdInput: c::INVALID_HANDLE_VALUE,
708-
hStdOutput: c::INVALID_HANDLE_VALUE,
709-
hStdError: c::INVALID_HANDLE_VALUE,
711+
fn zeroed_startupinfo_ex() -> c::STARTUPINFOEX {
712+
c::STARTUPINFOEX {
713+
StartupInfo: c::STARTUPINFO {
714+
cb: 0,
715+
lpReserved: ptr::null_mut(),
716+
lpDesktop: ptr::null_mut(),
717+
lpTitle: ptr::null_mut(),
718+
dwX: 0,
719+
dwY: 0,
720+
dwXSize: 0,
721+
dwYSize: 0,
722+
dwXCountChars: 0,
723+
dwYCountCharts: 0,
724+
dwFillAttribute: 0,
725+
dwFlags: 0,
726+
wShowWindow: 0,
727+
cbReserved2: 0,
728+
lpReserved2: ptr::null_mut(),
729+
hStdInput: c::INVALID_HANDLE_VALUE,
730+
hStdOutput: c::INVALID_HANDLE_VALUE,
731+
hStdError: c::INVALID_HANDLE_VALUE,
732+
},
733+
lpAttributeList: ptr::null_mut(),
710734
}
711735
}
712736

@@ -843,6 +867,57 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
843867
}
844868
}
845869

870+
/// Wrapper around a u8 buffer which ensures that a ProcThreadAttributeList
871+
/// is not moved during use and is not freed before calling Delete.
872+
struct ProcThreadAttributeList(Pin<Vec<u8>>);
873+
impl Drop for ProcThreadAttributeList {
874+
fn drop(&mut self) {
875+
unsafe { c::DeleteProcThreadAttributeList(self.0.as_mut_ptr().cast()) }
876+
}
877+
}
878+
/// Wrapper around the value data to be used as a Process Thread Attribute.
879+
/// This exists primarily to force the raw pointer type to be `Send` and `Sync`
880+
/// without needing to `unsafe impl` them for `Command`.
881+
#[derive(Copy, Clone)]
882+
struct ProcThreadAttributeValue {
883+
ptr: *mut c_void,
884+
size: usize,
885+
}
886+
unsafe impl Send for ProcThreadAttributeValue {}
887+
unsafe impl Sync for ProcThreadAttributeValue {}
888+
889+
fn make_proc_thread_attributes(
890+
attributes: &BTreeMap<usize, ProcThreadAttributeValue>,
891+
) -> io::Result<ProcThreadAttributeList> {
892+
let mut buffer_size = 0;
893+
let count = attributes.len() as u32;
894+
// First call to get required size. Error is expected.
895+
unsafe { c::InitializeProcThreadAttributeList(ptr::null_mut(), count, 0, &mut buffer_size) };
896+
let mut buf = Pin::new(crate::vec::from_elem(0u8, buffer_size as usize));
897+
// Second call to really initialize the buffer.
898+
cvt(unsafe {
899+
c::InitializeProcThreadAttributeList(buf.as_mut_ptr().cast(), count, 0, &mut buffer_size)
900+
})?;
901+
let mut attribute_list = ProcThreadAttributeList(buf);
902+
// Add our attributes to the buffer.
903+
// It's theoretically possible for the count to overflow a u32. Therefore, make
904+
// sure we don't add more attributes than we actually initialized the buffer for.
905+
for (&attribute, &value) in attributes.iter().take(count as usize) {
906+
cvt(unsafe {
907+
c::UpdateProcThreadAttribute(
908+
attribute_list.0.as_mut_ptr().cast(),
909+
0,
910+
attribute,
911+
value.ptr,
912+
value.size,
913+
ptr::null_mut(),
914+
ptr::null_mut(),
915+
)
916+
})?;
917+
}
918+
Ok(attribute_list)
919+
}
920+
846921
pub struct CommandArgs<'a> {
847922
iter: crate::slice::Iter<'a, Arg>,
848923
}

0 commit comments

Comments
 (0)