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

tcp: mitigate illegal state transitions on simultaneous close #279

Closed

Conversation

cbranch
Copy link
Contributor

@cbranch cbranch commented Mar 12, 2019

If a socket is closed and the remote side sends a FIN before the local
side sends its own FIN, it is possible to jump directly from FIN-WAIT-1
to CLOSING without ever sending a FIN to the remote side.

The main side effect is that if untransmitted data is present, the
socket is never exhausted, causing EthernetInterface::poll to loop
forever.

The ideal fix is to follow RFC more closely and not transition to
FIN-WAIT-1 until the FIN has been sent, but there are too many
assumptions based on the current state that would be broken by remaining
in ESTABLISHED with a closed transmit buffer to be worth fixing
a relatively rare edge case. Instead, add another illegal transition to
fix the failures of a previous violation, in the spirit of all good TCP
stacks.


The main reason why this is a problem at all is because poll performs ingress before egress, which is how this possibility can even arise. I haven't thought too hard about the implications of swapping them around. Maybe because, as already stated, this is a pretty rare occurrence to begin with.

If a socket is closed and the remote side sends a RST before the local
side sends its own RST, it is possible to jump directly from FIN-WAIT-1
to CLOSING without ever sending a RST to the remote side.

The main side effect is that if untransmitted data is present, the
socket is never exhausted, causing `EthernetInterface::poll` to loop
forever.

The ideal fix is to follow RFC more closely and not transition to
FIN-WAIT-1 until the RST has been sent, but there are too many
assumptions based on the current state that would be broken by remaining
in ESTABLISHED with a closed transmit buffer to be worth fixing
a relatively rare edge case. Instead, add another illegal transition to
fix the failures of a previous violation, in the spirit of all good TCP
stacks.
@whitequark
Copy link
Contributor

I acknowledge the issue, but I'll need to think about the fix.

@whitequark
Copy link
Contributor

Can you tell me a bit more about this fix? In particular you are talking about RST in the PR description, but I can't find how your code or tests use RST at all. Do you mean FIN?

@cbranch
Copy link
Contributor Author

cbranch commented Apr 26, 2019

Oof, that’s a dumb error. Please s/RST/FIN

@Dirbaio
Copy link
Member

Dirbaio commented Dec 27, 2020

Thank you for identifying this issue! This is definitely a bug in the state machine that needs fixing.

I believe the fix is incorrect though. Tracking whether we've sent a FIN is not enough: it may get lost, so we may have to retransmit it. Therefore we need to send FINs in the CLOSING state itself. Enabling the existing code to transmit FINs for CLOSING (in addition to FIN-WAIT-1 and LAST-ACK) fixes the issue both for the initial FIN and the retransmissions.

To speed things up I've opened #398 with that fix, so I'm closing this.

@Dirbaio Dirbaio closed this Dec 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants