Skip to content

Commit 529881a

Browse files
committed
Windows: Use anonymous pipes in Command
1 parent 64033a4 commit 529881a

File tree

4 files changed

+143
-84
lines changed

4 files changed

+143
-84
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,23 @@ unsafe extern "system" {
119119
pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL;
120120
}
121121

122+
windows_targets::link!("ntdll.dll" "system" fn NtCreateNamedPipeFile(
123+
filehandle: *mut HANDLE,
124+
desiredaccess: FILE_ACCESS_RIGHTS,
125+
objectattributes: *const OBJECT_ATTRIBUTES,
126+
iostatusblock: *mut IO_STATUS_BLOCK,
127+
shareaccess: FILE_SHARE_MODE,
128+
createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
129+
createoptions: NTCREATEFILE_CREATE_OPTIONS,
130+
namedpipetype: u32,
131+
readmode: u32,
132+
completionmode: u32,
133+
maximuminstances: u32,
134+
inboundquota: u32,
135+
outboundquota: u32,
136+
defaulttimeout: *const u64,
137+
) -> NTSTATUS);
138+
122139
// Functions that aren't available on every version of Windows that we support,
123140
// but we still use them and just provide some form of a fallback implementation.
124141
compat_fn_with_fallback! {

library/std/src/sys/pal/windows/c/bindings.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,6 +2060,14 @@ FILE_OPEN_REPARSE_POINT
20602060
FILE_OPEN_REQUIRING_OPLOCK
20612061
FILE_OVERWRITE
20622062
FILE_OVERWRITE_IF
2063+
FILE_PIPE_ACCEPT_REMOTE_CLIENTS
2064+
FILE_PIPE_BYTE_STREAM_MODE
2065+
FILE_PIPE_BYTE_STREAM_TYPE
2066+
FILE_PIPE_COMPLETE_OPERATION
2067+
FILE_PIPE_MESSAGE_MODE
2068+
FILE_PIPE_MESSAGE_TYPE
2069+
FILE_PIPE_QUEUE_OPERATION
2070+
FILE_PIPE_REJECT_REMOTE_CLIENTS
20632071
FILE_RANDOM_ACCESS
20642072
FILE_READ_ATTRIBUTES
20652073
FILE_READ_DATA
@@ -2294,7 +2302,16 @@ NtOpenFile
22942302
NtReadFile
22952303
NTSTATUS
22962304
NtWriteFile
2305+
OBJ_CASE_INSENSITIVE
22972306
OBJ_DONT_REPARSE
2307+
OBJ_EXCLUSIVE
2308+
OBJ_FORCE_ACCESS_CHECK
2309+
OBJ_IGNORE_IMPERSONATED_DEVICEMAP
2310+
OBJ_INHERIT
2311+
OBJ_KERNEL_HANDLE
2312+
OBJ_OPENIF
2313+
OBJ_OPENLINK
2314+
OBJ_PERMANENT
22982315
OPEN_ALWAYS
22992316
OPEN_EXISTING
23002317
OpenProcessToken

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Bindings generated by `windows-bindgen` 0.61.0
1+
// Bindings generated by `windows-bindgen` 0.61.1
22

33
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
44

@@ -2552,6 +2552,14 @@ pub const FILE_OPEN_REPARSE_POINT: NTCREATEFILE_CREATE_OPTIONS = 2097152u32;
25522552
pub const FILE_OPEN_REQUIRING_OPLOCK: NTCREATEFILE_CREATE_OPTIONS = 65536u32;
25532553
pub const FILE_OVERWRITE: NTCREATEFILE_CREATE_DISPOSITION = 4u32;
25542554
pub const FILE_OVERWRITE_IF: NTCREATEFILE_CREATE_DISPOSITION = 5u32;
2555+
pub const FILE_PIPE_ACCEPT_REMOTE_CLIENTS: u32 = 0u32;
2556+
pub const FILE_PIPE_BYTE_STREAM_MODE: u32 = 0u32;
2557+
pub const FILE_PIPE_BYTE_STREAM_TYPE: u32 = 0u32;
2558+
pub const FILE_PIPE_COMPLETE_OPERATION: u32 = 1u32;
2559+
pub const FILE_PIPE_MESSAGE_MODE: u32 = 1u32;
2560+
pub const FILE_PIPE_MESSAGE_TYPE: u32 = 1u32;
2561+
pub const FILE_PIPE_QUEUE_OPERATION: u32 = 0u32;
2562+
pub const FILE_PIPE_REJECT_REMOTE_CLIENTS: u32 = 2u32;
25552563
pub const FILE_RANDOM_ACCESS: NTCREATEFILE_CREATE_OPTIONS = 2048u32;
25562564
pub const FILE_READ_ATTRIBUTES: FILE_ACCESS_RIGHTS = 128u32;
25572565
pub const FILE_READ_DATA: FILE_ACCESS_RIGHTS = 1u32;
@@ -2983,7 +2991,16 @@ impl Default for OBJECT_ATTRIBUTES {
29832991
}
29842992
}
29852993
pub type OBJECT_ATTRIBUTE_FLAGS = u32;
2994+
pub const OBJ_CASE_INSENSITIVE: OBJECT_ATTRIBUTE_FLAGS = 64u32;
29862995
pub const OBJ_DONT_REPARSE: OBJECT_ATTRIBUTE_FLAGS = 4096u32;
2996+
pub const OBJ_EXCLUSIVE: OBJECT_ATTRIBUTE_FLAGS = 32u32;
2997+
pub const OBJ_FORCE_ACCESS_CHECK: OBJECT_ATTRIBUTE_FLAGS = 1024u32;
2998+
pub const OBJ_IGNORE_IMPERSONATED_DEVICEMAP: OBJECT_ATTRIBUTE_FLAGS = 2048u32;
2999+
pub const OBJ_INHERIT: OBJECT_ATTRIBUTE_FLAGS = 2u32;
3000+
pub const OBJ_KERNEL_HANDLE: OBJECT_ATTRIBUTE_FLAGS = 512u32;
3001+
pub const OBJ_OPENIF: OBJECT_ATTRIBUTE_FLAGS = 128u32;
3002+
pub const OBJ_OPENLINK: OBJECT_ATTRIBUTE_FLAGS = 256u32;
3003+
pub const OBJ_PERMANENT: OBJECT_ATTRIBUTE_FLAGS = 16u32;
29873004
pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32;
29883005
pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32;
29893006
#[repr(C)]

library/std/src/sys/pal/windows/pipe.rs

Lines changed: 91 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1-
use crate::ffi::OsStr;
21
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
2+
use crate::ops::Neg;
33
use crate::os::windows::prelude::*;
4-
use crate::path::Path;
5-
use crate::random::{DefaultRandomSource, Random};
6-
use crate::sync::atomic::Ordering::Relaxed;
7-
use crate::sync::atomic::{Atomic, AtomicUsize};
4+
use crate::sys::api::utf16;
85
use crate::sys::c;
9-
use crate::sys::fs::{File, OpenOptions};
106
use crate::sys::handle::Handle;
11-
use crate::sys::pal::windows::api::{self, WinError};
127
use crate::sys_common::{FromInner, IntoInner};
138
use crate::{mem, ptr};
149

@@ -65,89 +60,113 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
6560
// operations. Instead, we create a "hopefully unique" name and create a
6661
// named pipe which has overlapped operations enabled.
6762
//
68-
// Once we do this, we connect do it as usual via `CreateFileW`, and then
63+
// Once we do this, we connect to it via `NtOpenFile`, and then
6964
// we return those reader/writer halves. Note that the `ours` pipe return
7065
// value is always the named pipe, whereas `theirs` is just the normal file.
7166
// This should hopefully shield us from child processes which assume their
7267
// stdout is a named pipe, which would indeed be odd!
7368
unsafe {
74-
let ours;
75-
let mut name;
76-
let mut tries = 0;
77-
loop {
78-
tries += 1;
79-
name = format!(
80-
r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}",
81-
c::GetCurrentProcessId(),
82-
random_number(),
69+
let mut io_status = c::IO_STATUS_BLOCK::default();
70+
let mut object_attributes = c::OBJECT_ATTRIBUTES::default();
71+
object_attributes.Length = size_of::<c::OBJECT_ATTRIBUTES>() as u32;
72+
73+
// Open a handle to the pipe filesystem (`\??\PIPE\`).
74+
// This will be used when creating a new annon pipe.
75+
let pipe_fs = {
76+
let path = c::UNICODE_STRING::from_ref(utf16!(r"\??\PIPE\"));
77+
object_attributes.ObjectName = &path;
78+
let mut pipe_fs = ptr::null_mut();
79+
let status = c::NtOpenFile(
80+
&mut pipe_fs,
81+
c::SYNCHRONIZE | c::GENERIC_READ,
82+
&object_attributes,
83+
&mut io_status,
84+
c::FILE_SHARE_READ | c::FILE_SHARE_WRITE,
85+
c::FILE_SYNCHRONOUS_IO_NONALERT, // synchronous access
8386
);
84-
let wide_name = OsStr::new(&name).encode_wide().chain(Some(0)).collect::<Vec<_>>();
85-
let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE | c::FILE_FLAG_OVERLAPPED;
86-
if ours_readable {
87-
flags |= c::PIPE_ACCESS_INBOUND;
87+
if c::nt_success(status) {
88+
Handle::from_raw_handle(pipe_fs)
8889
} else {
89-
flags |= c::PIPE_ACCESS_OUTBOUND;
90+
return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32));
9091
}
92+
};
9193

92-
let handle = c::CreateNamedPipeW(
93-
wide_name.as_ptr(),
94-
flags,
95-
c::PIPE_TYPE_BYTE
96-
| c::PIPE_READMODE_BYTE
97-
| c::PIPE_WAIT
98-
| c::PIPE_REJECT_REMOTE_CLIENTS,
94+
// From now on we're not using handles instead of paths to create and open pipes.
95+
// So set the `ObjectName` to a zero length string.
96+
let empty = c::UNICODE_STRING::default();
97+
object_attributes.ObjectName = &empty;
98+
99+
// Create our side of the pipe for async access.
100+
let ours = {
101+
// Use the pipe filesystem as the root directory.
102+
// With no name provided, an anonymous pipe will be created.
103+
object_attributes.RootDirectory = pipe_fs.as_raw_handle();
104+
105+
// A negative timeout value is a relative time (rather than an absolute time).
106+
// The time is given in 100's of nanoseconds so this is 50 milliseconds.
107+
let timeout = (50_i64 * 10000).neg() as u64;
108+
109+
let mut ours = ptr::null_mut();
110+
let status = c::NtCreateNamedPipeFile(
111+
&mut ours,
112+
c::SYNCHRONIZE
113+
| if ours_readable {
114+
c::GENERIC_READ | c::SYNCHRONIZE
115+
} else {
116+
c::GENERIC_WRITE | c::SYNCHRONIZE
117+
},
118+
&object_attributes,
119+
&mut io_status,
120+
if ours_readable { c::FILE_SHARE_WRITE } else { c::FILE_SHARE_READ },
121+
c::FILE_CREATE,
122+
0,
123+
c::FILE_PIPE_BYTE_STREAM_TYPE,
124+
c::FILE_PIPE_BYTE_STREAM_MODE,
125+
c::FILE_PIPE_QUEUE_OPERATION,
126+
// only allow one client pipe
99127
1,
100128
PIPE_BUFFER_CAPACITY,
101129
PIPE_BUFFER_CAPACITY,
102-
0,
103-
ptr::null_mut(),
130+
&timeout,
104131
);
105-
106-
// We pass the `FILE_FLAG_FIRST_PIPE_INSTANCE` flag above, and we're
107-
// also just doing a best effort at selecting a unique name. If
108-
// `ERROR_ACCESS_DENIED` is returned then it could mean that we
109-
// accidentally conflicted with an already existing pipe, so we try
110-
// again.
111-
//
112-
// Don't try again too much though as this could also perhaps be a
113-
// legit error.
114-
if handle == c::INVALID_HANDLE_VALUE {
115-
let error = api::get_last_error();
116-
if tries < 10 && error == WinError::ACCESS_DENIED {
117-
continue;
118-
} else {
119-
return Err(io::Error::from_raw_os_error(error.code as i32));
120-
}
132+
if c::nt_success(status) {
133+
Handle::from_raw_handle(ours)
134+
} else {
135+
return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32));
121136
}
137+
};
122138

123-
ours = Handle::from_raw_handle(handle);
124-
break;
125-
}
139+
// Open their side of the pipe for synchronous access.
140+
let theirs = {
141+
// We can reopen the anonymous pipe without a name by setting
142+
// RootDirectory to the pipe handle and not setting a path name,
143+
object_attributes.RootDirectory = ours.as_raw_handle();
126144

127-
// Connect to the named pipe we just created. This handle is going to be
128-
// returned in `theirs`, so if `ours` is readable we want this to be
129-
// writable, otherwise if `ours` is writable we want this to be
130-
// readable.
131-
//
132-
// Additionally we don't enable overlapped mode on this because most
133-
// client processes aren't enabled to work with that.
134-
let mut opts = OpenOptions::new();
135-
opts.write(ours_readable);
136-
opts.read(!ours_readable);
137-
opts.share_mode(0);
138-
let size = size_of::<c::SECURITY_ATTRIBUTES>();
139-
let mut sa = c::SECURITY_ATTRIBUTES {
140-
nLength: size as u32,
141-
lpSecurityDescriptor: ptr::null_mut(),
142-
bInheritHandle: their_handle_inheritable as i32,
145+
if their_handle_inheritable {
146+
object_attributes.Attributes |= c::OBJ_INHERIT;
147+
}
148+
let mut theirs = ptr::null_mut();
149+
let status = c::NtOpenFile(
150+
&mut theirs,
151+
c::SYNCHRONIZE
152+
| if ours_readable {
153+
c::GENERIC_WRITE | c::FILE_READ_ATTRIBUTES
154+
} else {
155+
c::GENERIC_READ
156+
},
157+
&object_attributes,
158+
&mut io_status,
159+
0,
160+
c::FILE_NON_DIRECTORY_FILE | c::FILE_SYNCHRONOUS_IO_NONALERT,
161+
);
162+
if c::nt_success(status) {
163+
Handle::from_raw_handle(theirs)
164+
} else {
165+
return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32));
166+
}
143167
};
144-
opts.security_attributes(&mut sa);
145-
let theirs = File::open(Path::new(&name), &opts)?;
146168

147-
Ok(Pipes {
148-
ours: AnonPipe { inner: ours },
149-
theirs: AnonPipe { inner: theirs.into_inner() },
150-
})
169+
Ok(Pipes { ours: AnonPipe { inner: ours }, theirs: AnonPipe { inner: theirs } })
151170
}
152171
}
153172

@@ -191,17 +210,6 @@ pub fn spawn_pipe_relay(
191210
Ok(theirs)
192211
}
193212

194-
fn random_number() -> usize {
195-
static N: Atomic<usize> = AtomicUsize::new(0);
196-
loop {
197-
if N.load(Relaxed) != 0 {
198-
return N.fetch_add(1, Relaxed);
199-
}
200-
201-
N.store(usize::random(&mut DefaultRandomSource), Relaxed);
202-
}
203-
}
204-
205213
impl AnonPipe {
206214
pub fn handle(&self) -> &Handle {
207215
&self.inner

0 commit comments

Comments
 (0)