From b1f96fa6c301d52214cd2aeaa0a60f4f7958c973 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Tue, 1 Oct 2024 16:04:22 -0700 Subject: [PATCH] Avoid crashing the process when `Stream.Dispose` throws Fixes #1084 --- src/StreamJsonRpc/PipeMessageHandler.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/StreamJsonRpc/PipeMessageHandler.cs b/src/StreamJsonRpc/PipeMessageHandler.cs index 9d4d64c3e..3947a25bc 100644 --- a/src/StreamJsonRpc/PipeMessageHandler.cs +++ b/src/StreamJsonRpc/PipeMessageHandler.cs @@ -71,7 +71,7 @@ public PipeMessageHandler(Stream? writer, Stream? reader, IJsonRpcMessageFormatt { Assumes.NotNull(this.Writer); #pragma warning disable CS0618 // Type or member is obsolete (Nerdbank.Streams implements this, so it won't go away). - this.Writer.OnReaderCompleted((ex, state) => ((Stream)state!).Dispose(), writer); + this.Writer.OnReaderCompleted(static (ex, state) => DisposeStreamSafely(state), writer); #pragma warning restore CS0618 // Type or member is obsolete } @@ -80,7 +80,20 @@ public PipeMessageHandler(Stream? writer, Stream? reader, IJsonRpcMessageFormatt // We only need to do this if the read stream is distinct from the write stream, which is already handled above. if (reader is not null && reader != writer) { - this.DisposalToken.Register(state => ((Stream)state!).Dispose(), reader); + this.DisposalToken.Register(DisposeStreamSafely, reader); + } + + static void DisposeStreamSafely(object? state) + { + // These callbacks are invoked on a worker thread with no exception handler underneath. + try + { + ((Stream?)state)?.Dispose(); + } + catch (Exception) + { + // Dispose should never throw, but if it does, we don't want the process to crash. + } } }