Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Wrong exception for Open*Stream after connection is closed #67342

Merged
merged 3 commits into from
Mar 31, 2022

Conversation

rzikm
Copy link
Member

@rzikm rzikm commented Mar 30, 2022

I considered adding guards into individual Open*Stream methods, but that still leaves a data race between the stream-creating thread and incoming connection aborts, so it seems best to me to use the status code from MsQuicStreamOpen to detect connection abort/close.

Fixes #56133

@ghost
Copy link

ghost commented Mar 30, 2022

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details

I considered adding guards into individual Open*Stream methods, but that still leaves a data race between the stream-creating thread and incoming connection aborts, so I devided it would be best to use the status code from MsQuicStreamOpen.

Fixes #56133

Author: rzikm
Assignees: rzikm
Labels:

area-System.Net.Quic

Milestone: -

@@ -173,6 +173,12 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F
GCHandle.ToIntPtr(_state.StateGCHandle),
out _state.Handle);

if (status == MsQuicStatusCodes.Aborted || status == MsQuicStatusCodes.InvalidState)
Copy link
Member

@CarnaViire CarnaViire Mar 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should throw QuicConnectionAbortedException only if status == MsQuicStatusCodes.Aborted. If it's an invalid state, it's a sign that there's a bug left either in our code or in msquic -- we don't want to hide that. And connectionState.AbortErrorCode would be set only if connection was actually aborted.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the InvalidState happens when the connection is aborted locally (i.e. first close the connection and then immediately try opening a stream), so to cover that case without checking for InvalidState here, we would probably have to put another guard somewhere else (like Open*Stream function).

Copy link
Member

@CarnaViire CarnaViire Mar 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the connection is aborted locally, it should be different exception (QuicOperationAbortedException) -- this is how user understands who was the initiator...
UPD: Ok, ThrowHelper already does that check

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you seen INVALID_STATE happening on local close?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I see
https://github.com/microsoft/msquic/blob/0fffdc076abd52a61a2d4d8d699cf6a5f80e56e5/src/core/stream.c#L221-L223

        Status = 
            (ClosedLocally || Stream->Flags.Started) ?
            QUIC_STATUS_INVALID_STATE :
            QUIC_STATUS_ABORTED;

but it means INVALID_STATE is not only used if ClosedLocally; and it doesn't prevent stream_open from returning it later for some different reason....

On one hand, it will be better if we were able to tell the difference and not just translate INVALID_STATE into OperationAbortedException. This exception now bears the meaning of "you do something that results in a local operation being aborted, e.g. AbortRead (or close Stream or close Connection) with a pending Read"; if we use it for something else it will become unhelpful... in the same way "INVALID_STATE" sounds unhelpful -- something is wrong but you're not sure what.

On the other hand, tracking all we do locally and adding the guards you mention might be too big and complex change, and if it would involve locking, it may significantly impact perf.

I was initially inclined towards not translating INVALID_STATE into OperationAbortedException, but I'm not so sure now.

@ManickaP what are your thoughts?

Copy link
Member

@ManickaP ManickaP Mar 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of INVALID_STATE we're doing something wrong (we = user of System.Net.Quic), 2 cases here:

  • closing connection locally and then opening stream
  • opening stream twice

In both cases, we're fully responsible for the wrong combination of operations.

==> I think we should keep the original exception for INVALID_STATE and change it only for ABORTED.

Note that we might revisit this in the future (APIs, customer feedback, ...), so no need to obsess about it.

Copy link
Member

@CarnaViire CarnaViire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

…ons/MsQuic/MsQuicStream.cs

Co-authored-by: Natalia Kondratyeva <knatalia@microsoft.com>
@rzikm rzikm merged commit a847ddf into dotnet:main Mar 31, 2022
@karelz karelz added this to the 7.0.0 milestone Apr 8, 2022
@ghost ghost locked as resolved and limited conversation to collaborators May 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

QUIC: Wrong exception for Open*Stream after connection is closed
4 participants