@@ -57,6 +57,8 @@ ep_rt_object_free (void *ptr)
57
57
}
58
58
#endif /* !FEATURE_PERFTRACING_STANDALONE_PAL */
59
59
60
+ static HANDLE _ipc_listen_ownership_handle = INVALID_HANDLE_VALUE ;
61
+
60
62
/*
61
63
* Forward declares of all static functions.
62
64
*/
@@ -91,6 +93,18 @@ static
91
93
bool
92
94
ipc_stream_close_func (void * object );
93
95
96
+ static
97
+ void
98
+ ipc_close_ownership_handle (
99
+ ds_ipc_error_callback_func callback );
100
+
101
+ static
102
+ bool
103
+ ipc_createpipe_helper (
104
+ DiagnosticsIpc * ipc ,
105
+ bool ensure_pipe_creation ,
106
+ ds_ipc_error_callback_func callback );
107
+
94
108
static
95
109
DiagnosticsIpcStream *
96
110
ipc_stream_alloc (
@@ -108,8 +122,9 @@ ds_ipc_pal_init (void)
108
122
}
109
123
110
124
bool
111
- ds_ipc_pal_shutdown (void )
125
+ ds_ipc_pal_shutdown (ds_ipc_error_callback_func callback )
112
126
{
127
+ ipc_close_ownership_handle (callback );
113
128
return true;
114
129
}
115
130
@@ -330,9 +345,11 @@ ds_ipc_poll (
330
345
ep_exit_error_handler ();
331
346
}
332
347
348
+ static
333
349
bool
334
- ds_ipc_listen (
350
+ ipc_createpipe_helper (
335
351
DiagnosticsIpc * ipc ,
352
+ bool ensure_pipe_creation ,
336
353
ds_ipc_error_callback_func callback )
337
354
{
338
355
bool result = false;
@@ -348,16 +365,41 @@ ds_ipc_listen (
348
365
if (ipc -> is_listening )
349
366
return true;
350
367
351
- EP_ASSERT (ipc -> pipe == INVALID_HANDLE_VALUE );
368
+ if (!ensure_pipe_creation && _ipc_listen_ownership_handle == INVALID_HANDLE_VALUE )
369
+ {
370
+ if (callback )
371
+ callback ("Can't ensure we have ownership of the pipe. Disallowing creation." , -1 );
372
+ return false;
373
+ }
374
+
375
+ if (ensure_pipe_creation && _ipc_listen_ownership_handle != INVALID_HANDLE_VALUE )
376
+ {
377
+ if (callback )
378
+ callback ("Inconsistent state - pipe sentinel already in use for listen." , -1 );
379
+ return false;
380
+ }
381
+
382
+ EP_ASSERT (ipc -> pipe == INVALID_HANDLE_VALUE );
352
383
353
384
const uint32_t in_buffer_size = 16 * 1024 ;
354
385
const uint32_t out_buffer_size = 16 * 1024 ;
355
386
387
+ DWORD creationFlags = PIPE_ACCESS_DUPLEX // read/write access
388
+ | FILE_FLAG_OVERLAPPED ; // async listening.
389
+
390
+ if (ensure_pipe_creation )
391
+ {
392
+ // Fail if we can't own pipe. This is the only way to ensure
393
+ // ownership of the pipe, and by extension the default DACL.
394
+ // Otherwise, Windows treats this as a FIFO queue get-or-create
395
+ // request and we might end up with DACLs set by other creators.
396
+ creationFlags |= FILE_FLAG_FIRST_PIPE_INSTANCE ;
397
+ }
398
+
356
399
DS_ENTER_BLOCKING_PAL_SECTION ;
357
400
ipc -> pipe = CreateNamedPipeA (
358
401
ipc -> pipe_name , // pipe name
359
- PIPE_ACCESS_DUPLEX | // read/write access
360
- FILE_FLAG_OVERLAPPED , // async listening
402
+ creationFlags ,
361
403
PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS , // message type pipe, message-read and blocking mode
362
404
PIPE_UNLIMITED_INSTANCES , // max. instances
363
405
out_buffer_size , // output buffer size
@@ -372,6 +414,28 @@ ds_ipc_listen (
372
414
ep_raise_error ();
373
415
}
374
416
417
+ if (ensure_pipe_creation )
418
+ {
419
+ EP_ASSERT (_ipc_listen_ownership_handle == INVALID_HANDLE_VALUE );
420
+
421
+ // The dupe and leak of the handle to ensure listen EP ownership for process duration.
422
+ bool createdSentinel = DuplicateHandle (
423
+ GetCurrentProcess (),
424
+ ipc -> pipe ,
425
+ GetCurrentProcess (),
426
+ & _ipc_listen_ownership_handle ,
427
+ 0 ,
428
+ FALSE,
429
+ DUPLICATE_SAME_ACCESS );
430
+
431
+ if (!createdSentinel )
432
+ {
433
+ if (callback )
434
+ callback ("Failed to ownership sentinel." , GetLastError ());
435
+ ep_raise_error ();
436
+ }
437
+ }
438
+
375
439
EP_ASSERT (ipc -> overlap .hEvent == INVALID_HANDLE_VALUE );
376
440
377
441
ipc -> overlap .hEvent = CreateEventW (NULL , true, false, NULL );
@@ -407,10 +471,23 @@ ds_ipc_listen (
407
471
408
472
ep_on_error :
409
473
ds_ipc_close (ipc , false, callback );
474
+ if (ensure_pipe_creation )
475
+ ipc_close_ownership_handle (callback );
410
476
result = false;
411
477
ep_exit_error_handler ();
412
478
}
413
479
480
+ bool
481
+ ds_ipc_listen (
482
+ DiagnosticsIpc * ipc ,
483
+ ds_ipc_error_callback_func callback )
484
+ {
485
+ // This is the first time that this listening channel is created
486
+ // from the perspective of the runtime. Request we ensure that we create
487
+ // the pipe.
488
+ return ipc_createpipe_helper (ipc , true, callback );
489
+ }
490
+
414
491
DiagnosticsIpcStream *
415
492
ds_ipc_accept (
416
493
DiagnosticsIpc * ipc ,
@@ -459,7 +536,10 @@ ds_ipc_accept (
459
536
memset (& ipc -> overlap , 0 , sizeof (OVERLAPPED )); // clear the overlapped objects state
460
537
ipc -> overlap .hEvent = INVALID_HANDLE_VALUE ;
461
538
462
- ep_raise_error_if_nok (ds_ipc_listen (ipc , callback ));
539
+ // At this point we have at least one open connection with this pipe,
540
+ // so this listen pipe won't recreate the named pipe and thus inherit
541
+ // all the necessary DACLs from the original listen call.
542
+ ep_raise_error_if_nok (ipc_createpipe_helper (ipc , false, callback ));
463
543
464
544
ep_on_exit :
465
545
return stream ;
@@ -526,6 +606,27 @@ ds_ipc_connect (
526
606
ep_exit_error_handler ();
527
607
}
528
608
609
+ void
610
+ ipc_close_ownership_handle (
611
+ ds_ipc_error_callback_func callback )
612
+ {
613
+ if (_ipc_listen_ownership_handle == INVALID_HANDLE_VALUE )
614
+ return ;
615
+
616
+ const BOOL success_close_pipe = CloseHandle (_ipc_listen_ownership_handle );
617
+ if (success_close_pipe != TRUE)
618
+ {
619
+ if (callback )
620
+ callback ("Failed to IPC ownership sentinel handle" , GetLastError ());
621
+ // Explicitly don't reset it. Failing to close and setting it to an invalid handle
622
+ // leaks the handle in a way we can't diagnose anything. Leaving it rooted helps us
623
+ // assert state consistency.
624
+ return ;
625
+ }
626
+
627
+ _ipc_listen_ownership_handle = INVALID_HANDLE_VALUE ;
628
+ }
629
+
529
630
void
530
631
ds_ipc_close (
531
632
DiagnosticsIpc * ipc ,
@@ -535,7 +636,9 @@ ds_ipc_close (
535
636
EP_ASSERT (ipc != NULL );
536
637
537
638
// don't attempt cleanup on shutdown and let the OS handle it
538
- if (is_shutdown ) {
639
+ // except in the case of listen pipes - if they leak the process
640
+ // will fail to reinitialize the pipe for embedding scenarios.
641
+ if (is_shutdown && ipc -> mode != DS_IPC_CONNECTION_MODE_LISTEN ) {
539
642
if (callback )
540
643
callback ("Closing without cleaning underlying handles" , 100 );
541
644
return ;
0 commit comments