From 08c2a0c0feb367471960282573de8cce72d0513d Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Mon, 28 Oct 2024 19:41:09 +0100 Subject: [PATCH 1/2] support raw_attribute with raw pointer --- library/std/src/os/windows/process.rs | 22 ++++++ library/std/src/sys/pal/windows/process.rs | 82 +++++++++++++++++----- 2 files changed, 87 insertions(+), 17 deletions(-) diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index c2830d2eb61d1..11df3a477fc7c 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -4,6 +4,8 @@ #![stable(feature = "process_extensions", since = "1.2.0")] +use core::ffi::c_void; + use crate::ffi::OsStr; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, @@ -368,6 +370,14 @@ pub trait CommandExt: Sealed { attribute: usize, value: T, ) -> &mut process::Command; + + #[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] + unsafe fn raw_attribute_ptr( + &mut self, + attribute: usize, + value_ptr: *const c_void, + value_size: usize, + ) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -409,6 +419,18 @@ impl CommandExt for process::Command { unsafe { self.as_inner_mut().raw_attribute(attribute, value) }; self } + + unsafe fn raw_attribute_ptr( + &mut self, + attribute: usize, + value_ptr: *const c_void, + value_size: usize, + ) -> &mut process::Command { + unsafe { + self.as_inner_mut().raw_attribute_ptr(attribute, value_ptr, value_size); + } + self + } } #[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 17bb03fe7af04..480ebe37050a3 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -253,10 +253,28 @@ impl Command { attribute: usize, value: T, ) { - self.proc_thread_attributes.insert(attribute, ProcThreadAttributeValue { - size: mem::size_of::(), - data: Box::new(value), - }); + self.proc_thread_attributes.insert( + attribute, + ProcThreadAttributeValue::Data(ProcThreadAttributeValueData { + size: mem::size_of::(), + data: Box::new(value), + }), + ); + } + + pub unsafe fn raw_attribute_ptr( + &mut self, + attribute: usize, + value_ptr: *const c_void, + value_size: usize, + ) { + self.proc_thread_attributes.insert( + attribute, + ProcThreadAttributeValue::Pointer(ProcThreadAttributeValuePointer { + size: value_size, + pointer: value_ptr as isize, + }), + ); } pub fn spawn( @@ -907,11 +925,21 @@ impl Drop for ProcThreadAttributeList { } /// Wrapper around the value data to be used as a Process Thread Attribute. -struct ProcThreadAttributeValue { +struct ProcThreadAttributeValueData { data: Box, size: usize, } +struct ProcThreadAttributeValuePointer { + pointer: isize, // using isize instead of *const c_void to have it sendable + size: usize, +} + +enum ProcThreadAttributeValue { + Data(ProcThreadAttributeValueData), + Pointer(ProcThreadAttributeValuePointer), +} + fn make_proc_thread_attribute_list( attributes: &BTreeMap, ) -> io::Result { @@ -953,18 +981,38 @@ fn make_proc_thread_attribute_list( // It's theoretically possible for the attribute count to exceed a u32 value. // Therefore, we ensure that we don't add more attributes than the buffer was initialized for. for (&attribute, value) in attributes.iter().take(attribute_count as usize) { - let value_ptr = (&raw const *value.data) as _; - cvt(unsafe { - c::UpdateProcThreadAttribute( - proc_thread_attribute_list.0.as_mut_ptr() as _, - 0, - attribute, - value_ptr, - value.size, - ptr::null_mut(), - ptr::null_mut(), - ) - })?; + match value { + ProcThreadAttributeValue::Data(value) => { + let value_ptr = (&raw const *value.data) as _; + cvt(unsafe { + c::UpdateProcThreadAttribute( + proc_thread_attribute_list.0.as_mut_ptr() as _, + 0, + attribute, + value_ptr, + value.size, + ptr::null_mut(), + ptr::null_mut(), + ) + })?; + } + ProcThreadAttributeValue::Pointer(value) => { + cvt( + unsafe { + #![allow(fuzzy_provenance_casts)] + c::UpdateProcThreadAttribute( + proc_thread_attribute_list.0.as_mut_ptr() as _, + 0, + attribute, + value.pointer as *const c_void, + value.size, + ptr::null_mut(), + ptr::null_mut(), + ) + }, + )?; + } + } } Ok(proc_thread_attribute_list) From 1f4ba49385481e342ea5e6ab590898a1ebb8b67b Mon Sep 17 00:00:00 2001 From: Ulrich Hornung Date: Sat, 20 Apr 2024 19:04:15 +0200 Subject: [PATCH 2/2] set c::STARTF_USESTDHANDLES when PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE --- library/std/src/sys/pal/windows/process.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 480ebe37050a3..ac985e104e3c8 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -354,12 +354,21 @@ impl Command { let mut si = zeroed_startupinfo(); + // if STARTF_USESTDHANDLES is not used with PSEUDOCONSOLE, + // it is not guaranteed that all the desired stdio of the new process are + // really connected to the new console. + // The problem + solution is described here: + // https://github.com/microsoft/terminal/issues/4380#issuecomment-580865346 + const PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: usize = 0x20016; + let force_use_std_handles = + self.proc_thread_attributes.contains_key(&PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE); + // If at least one of stdin, stdout or stderr are set (i.e. are non null) // then set the `hStd` fields in `STARTUPINFO`. // Otherwise skip this and allow the OS to apply its default behavior. // This provides more consistent behavior between Win7 and Win8+. let is_set = |stdio: &Handle| !stdio.as_raw_handle().is_null(); - if is_set(&stderr) || is_set(&stdout) || is_set(&stdin) { + if force_use_std_handles || is_set(&stderr) || is_set(&stdout) || is_set(&stdin) { si.dwFlags |= c::STARTF_USESTDHANDLES; si.hStdInput = stdin.as_raw_handle(); si.hStdOutput = stdout.as_raw_handle();