@@ -37,7 +37,8 @@ use windows::{
37
37
} ,
38
38
Storage :: FileSystem :: {
39
39
CreateFileA , ReadFile , WriteFile , FILE_ATTRIBUTE_NORMAL , FILE_FLAG_OVERLAPPED ,
40
- FILE_GENERIC_WRITE , FILE_SHARE_MODE , OPEN_EXISTING , PIPE_ACCESS_INBOUND ,
40
+ FILE_GENERIC_READ , FILE_GENERIC_WRITE , FILE_SHARE_MODE , OPEN_EXISTING ,
41
+ PIPE_ACCESS_DUPLEX ,
41
42
} ,
42
43
System :: {
43
44
Memory :: {
@@ -61,8 +62,12 @@ use windows::{
61
62
} ;
62
63
63
64
mod aliased_cell;
65
+
64
66
use self :: aliased_cell:: AliasedCell ;
65
67
68
+ #[ cfg( test) ]
69
+ mod tests;
70
+
66
71
lazy_static ! {
67
72
static ref CURRENT_PROCESS_ID : u32 = unsafe { GetCurrentProcessId ( ) } ;
68
73
static ref CURRENT_PROCESS_HANDLE : WinHandle = WinHandle :: new( unsafe { GetCurrentProcess ( ) } ) ;
@@ -126,6 +131,32 @@ pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver), WinError> {
126
131
Ok ( ( sender, receiver) )
127
132
}
128
133
134
+ /// Unify the creation of sender and receiver duplex pipes to allow for either to be spawned first.
135
+ /// Requires the use of a duplex and therefore lets both sides read and write.
136
+ unsafe fn create_duplex ( pipe_name : & CString ) -> Result < HANDLE , WinError > {
137
+ CreateFileA (
138
+ PCSTR :: from_raw ( pipe_name. as_ptr ( ) as * const u8 ) ,
139
+ FILE_GENERIC_WRITE . 0 | FILE_GENERIC_READ . 0 ,
140
+ FILE_SHARE_MODE ( 0 ) ,
141
+ None , // lpSecurityAttributes
142
+ OPEN_EXISTING ,
143
+ FILE_ATTRIBUTE_NORMAL ,
144
+ None ,
145
+ )
146
+ . or ( CreateNamedPipeA (
147
+ PCSTR :: from_raw ( pipe_name. as_ptr ( ) as * const u8 ) ,
148
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED ,
149
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS ,
150
+ // 1 max instance of this pipe
151
+ 1 ,
152
+ // out/in buffer sizes
153
+ 0 ,
154
+ PIPE_BUFFER_SIZE as u32 ,
155
+ 0 , // default timeout for WaitNamedPipe (0 == 50ms as default)
156
+ None ,
157
+ ) )
158
+ }
159
+
129
160
struct MessageHeader {
130
161
data_len : u32 ,
131
162
oob_len : u32 ,
@@ -144,7 +175,7 @@ struct Message<'data> {
144
175
}
145
176
146
177
impl < ' data > Message < ' data > {
147
- fn from_bytes ( bytes : & ' data [ u8 ] ) -> Option < Message > {
178
+ fn from_bytes ( bytes : & ' data [ u8 ] ) -> Option < Message < ' data > > {
148
179
if bytes. len ( ) < mem:: size_of :: < MessageHeader > ( ) {
149
180
return None ;
150
181
}
@@ -174,14 +205,11 @@ impl<'data> Message<'data> {
174
205
175
206
fn oob_data ( & self ) -> Option < OutOfBandMessage > {
176
207
if self . oob_len > 0 {
177
- let oob = bincode:: deserialize :: < OutOfBandMessage > ( self . oob_bytes ( ) )
208
+ let mut oob = bincode:: deserialize :: < OutOfBandMessage > ( self . oob_bytes ( ) )
178
209
. expect ( "Failed to deserialize OOB data" ) ;
179
- if oob. target_process_id != * CURRENT_PROCESS_ID {
180
- panic ! ( "Windows IPC channel received handles intended for pid {}, but this is pid {}. \
181
- This likely happened because a receiver was transferred while it had outstanding data \
182
- that contained a channel or shared memory in its pipe. \
183
- This isn't supported in the Windows implementation.",
184
- oob. target_process_id, * CURRENT_PROCESS_ID ) ;
210
+ if let Err ( e) = oob. recover_handles ( ) {
211
+ win32_trace ! ( "Failed to recover handles: {:?}" , e) ;
212
+ return None ;
185
213
}
186
214
Some ( oob)
187
215
} else {
@@ -202,18 +230,7 @@ impl<'data> Message<'data> {
202
230
/// in another channel's buffer when that channel got transferred to another
203
231
/// process. On Windows, we duplicate handles on the sender side to a specific
204
232
/// receiver. If the wrong receiver gets it, those handles are not valid.
205
- ///
206
- /// TODO(vlad): We could attempt to recover from the above situation by
207
- /// duplicating from the intended target process to ourselves (the receiver).
208
- /// That would only work if the intended process a) still exists; b) can be
209
- /// opened by the receiver with handle dup privileges. Another approach
210
- /// could be to use a separate dedicated process intended purely for handle
211
- /// passing, though that process would need to be global to any processes
212
- /// amongst which you want to share channels or connect one-shot servers to.
213
- /// There may be a system process that we could use for this purpose, but
214
- /// I haven't found one -- and in the system process case, we'd need to ensure
215
- /// that we don't leak the handles (e.g. dup a handle to the system process,
216
- /// and then everything dies -- we don't want those resources to be leaked).
233
+ /// These handles are recovered by the `recover_handles` method.
217
234
#[ derive( Debug ) ]
218
235
struct OutOfBandMessage {
219
236
target_process_id : u32 ,
@@ -237,6 +254,70 @@ impl OutOfBandMessage {
237
254
|| !self . shmem_handles . is_empty ( )
238
255
|| self . big_data_receiver_handle . is_some ( )
239
256
}
257
+
258
+ /// Recover handles that are no longer valid in the current process via duplication.
259
+ /// Duplicates the handle from the target process to the current process.
260
+ fn recover_handles ( & mut self ) -> Result < ( ) , WinError > {
261
+ // get current process id and target process.
262
+ let current_process = unsafe { GetCurrentProcess ( ) } ;
263
+ let target_process =
264
+ unsafe { OpenProcess ( PROCESS_DUP_HANDLE , false , self . target_process_id ) ? } ;
265
+
266
+ // Duplicate channel handles.
267
+ for handle in & mut self . channel_handles {
268
+ let mut new_handle = INVALID_HANDLE_VALUE ;
269
+ unsafe {
270
+ DuplicateHandle (
271
+ target_process,
272
+ HANDLE ( * handle as _ ) ,
273
+ current_process,
274
+ & mut new_handle,
275
+ 0 ,
276
+ false ,
277
+ DUPLICATE_SAME_ACCESS ,
278
+ ) ?;
279
+ }
280
+ * handle = new_handle. 0 as isize ;
281
+ }
282
+
283
+ // Duplicate any shmem handles.
284
+ for ( handle, _) in & mut self . shmem_handles {
285
+ let mut new_handle = INVALID_HANDLE_VALUE ;
286
+ unsafe {
287
+ DuplicateHandle (
288
+ target_process,
289
+ HANDLE ( * handle as _ ) ,
290
+ current_process,
291
+ & mut new_handle,
292
+ 0 ,
293
+ false ,
294
+ DUPLICATE_SAME_ACCESS ,
295
+ ) ?;
296
+ }
297
+ * handle = new_handle. 0 as isize ;
298
+ }
299
+
300
+ // Duplicate any big data receivers.
301
+ if let Some ( ( handle, _) ) = & mut self . big_data_receiver_handle {
302
+ let mut new_handle = INVALID_HANDLE_VALUE ;
303
+ unsafe {
304
+ DuplicateHandle (
305
+ target_process,
306
+ HANDLE ( * handle as _ ) ,
307
+ current_process,
308
+ & mut new_handle,
309
+ 0 ,
310
+ false ,
311
+ DUPLICATE_SAME_ACCESS ,
312
+ ) ?;
313
+ }
314
+ * handle = new_handle. 0 as isize ;
315
+ }
316
+
317
+ // Close process handle.
318
+ unsafe { CloseHandle ( target_process) ? } ;
319
+ Ok ( ( ) )
320
+ }
240
321
}
241
322
242
323
impl serde:: Serialize for OutOfBandMessage {
@@ -1071,18 +1152,7 @@ impl OsIpcReceiver {
1071
1152
fn new_named ( pipe_name : & CString ) -> Result < OsIpcReceiver , WinError > {
1072
1153
unsafe {
1073
1154
// create the pipe server
1074
- let handle = CreateNamedPipeA (
1075
- PCSTR :: from_raw ( pipe_name. as_ptr ( ) as * const u8 ) ,
1076
- PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED ,
1077
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS ,
1078
- // 1 max instance of this pipe
1079
- 1 ,
1080
- // out/in buffer sizes
1081
- 0 ,
1082
- PIPE_BUFFER_SIZE as u32 ,
1083
- 0 , // default timeout for WaitNamedPipe (0 == 50ms as default)
1084
- None ,
1085
- ) ?;
1155
+ let handle = create_duplex ( pipe_name) ?;
1086
1156
1087
1157
Ok ( OsIpcReceiver {
1088
1158
reader : RefCell :: new ( MessageReader :: new ( WinHandle :: new ( handle) ) ) ,
@@ -1253,15 +1323,7 @@ impl OsIpcSender {
1253
1323
/// Connect to a pipe server.
1254
1324
fn connect_named ( pipe_name : & CString ) -> Result < OsIpcSender , WinError > {
1255
1325
unsafe {
1256
- let handle = CreateFileA (
1257
- PCSTR :: from_raw ( pipe_name. as_ptr ( ) as * const u8 ) ,
1258
- FILE_GENERIC_WRITE . 0 ,
1259
- FILE_SHARE_MODE ( 0 ) ,
1260
- None , // lpSecurityAttributes
1261
- OPEN_EXISTING ,
1262
- FILE_ATTRIBUTE_NORMAL ,
1263
- None ,
1264
- ) ?;
1326
+ let handle = create_duplex ( pipe_name) ?;
1265
1327
1266
1328
win32_trace ! ( "[c {:?}] connect_to_server success" , handle) ;
1267
1329
0 commit comments