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

Do not TLS close_notify when resetting a TCP connection #1944

Conversation

eduard-bagdasaryan
Copy link
Contributor

No description provided.

eduard-bagdasaryan and others added 7 commits November 4, 2024 16:21
Neither is perfect, but I think the earlier version of this
documentation gave the reader more/better information about this flag
intent/purpose. Here, SO_LINGER is just an implementation detail that
`.h` reader should not really care/know about.
Copy link
Contributor

@rousskov rousskov left a comment

Choose a reason for hiding this comment

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

This review annotates this PR without requesting changes.

Comment on lines +882 to 884
if (F->ssl && !F->flags.harshClosureRequested) {
const auto startCall = asyncCall(5, 4, "commStartTlsClose",
callDialer(commStartTlsClose, fd));
Copy link
Contributor

Choose a reason for hiding this comment

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

To me, it seems fairly obvious that if we are going to send TCP RST to the client, then we do not want to nicely close TLS session either. Our harsh closure reasons that apply to TCP layer ought to apply to TLS layer as well. For example, if Squid is sending the client an unchunked HTTP response without Content-Length header, and Squid has to abort that transaction, then we do not want successful TLS closure to trick that client into thinking that it has gotten the entire response body.

If others disagree with this "fairly obvious" assertion, then we should add an explanation to PR description. That explanation may be similar to the above paragraph (sans the "obvious" claim itself, of course).

Copy link
Contributor

Choose a reason for hiding this comment

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

While what you write seems reasonable, it is not clear that RST is the only result of this function being called. It can and is also called for the FIN cases where TLS should be completed cleanly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

RST is the only result of this function being called

FWIW, the function (commStartTlsClose()) is not called for RST cases (F->flags.harshClosureRequested is false) but called for other (i.e., FIN) cases, as you noted.

Copy link
Contributor

@rousskov rousskov Nov 14, 2024

Choose a reason for hiding this comment

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

Amos: it is not clear that RST is the only result of this function being called. It can and is also called for the FIN cases where TLS should be completed cleanly.

If "this function" is commStartTlsClose():

  • In official code, before this PR: commStartTlsClose() was called in RST and FIN cases, and that was wrong.
  • In PR code, commStartTlsClose() is only called in FIN cases, as intended.
  • RST is not the "result of calling this function".

If "this function" is commConfigureLinger():

  • In official code, before this PR: commConfigureLinger() was called in both RST and FIN cases. That function call had no effect on commStartTlsClose() calls, and that was wrong.
  • In PR code: commConfigureLinger() is called in both RST and FIN cases. That function call bans future commStartTlsClose() calls in RST cases only, as intended.
  • RST is the "result of calling this function" with On parameter, followed by a close(2) system call.

It is, of course, possible that some code paths that end with commStartTlsClose() are missing a commConfigureLinger() call to RST the connection, but that possible problem is outside this PR scope. This PR does not change and should not change when/where commConfigureLinger() is called.

@@ -783,6 +783,8 @@ commConfigureLinger(const int fd, const OnOff enabled)
l.l_onoff = (enabled == OnOff::on ? 1 : 0);
l.l_linger = 0; // how long to linger for, in seconds

fd_table[fd].flags.harshClosureRequested = (l.l_onoff && !l.l_linger); // close(2) sends TCP RST if true

if (setsockopt(fd, SOL_SOCKET, SO_LINGER, reinterpret_cast<char*>(&l), sizeof(l)) < 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It may be tempting to make new harshClosureRequested flag assignment conditional on this setsockopt() call success, but I do not think we should do that: The caller has requested harsh connection closing; setsockopt() success is pretty much irrelevant. Even if TCP layer closes nicely despite our attempt to close harshly, we still want to close TLS layer harshly...

@@ -783,6 +783,8 @@ commConfigureLinger(const int fd, const OnOff enabled)
l.l_onoff = (enabled == OnOff::on ? 1 : 0);
l.l_linger = 0; // how long to linger for, in seconds

fd_table[fd].flags.harshClosureRequested = (l.l_onoff && !l.l_linger); // close(2) sends TCP RST if true
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a low-level private function with callers already checking fd, so I think it may be OK to avoid checking fd value before accessing fd_table here. If you disagree, consider applying this suggestion:

Suggested change
fd_table[fd].flags.harshClosureRequested = (l.l_onoff && !l.l_linger); // close(2) sends TCP RST if true
if (isOpen(fd))
fd_table[fd].flags.harshClosureRequested = (l.l_onoff && !l.l_linger); // close(2) sends TCP RST if true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We would need also to protect setsockopt() by the same check, i.e., basically this function should immediately return if isOpen() is false. I would just assert(isOpen(fd)) instead.

Copy link
Contributor

@rousskov rousskov Nov 14, 2024

Choose a reason for hiding this comment

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

We would need also to protect setsockopt() by the same check.

Not necessarily: Leaving that system call exposed to caller bugs may be better in this case because it will result in level-0 ERROR message, exposing the bug.

I would just assert(isOpen(fd)) instead.

I would too, but "The following [isOpen(fd) check] fails because ipc.c is doing calls to pipe() to create sockets" comment in _comm_close() worries me. That old comment may no longer reflect what is actually going on, of course, but it is a red flag.

All these complications/problems are outside this PR scope IMO (as detailed at the beginning of this thread). They are best resolved in a dedicated PR. That is why I did not recommend any changes in my review. Said that, if others insist on an if check or assertion, let's add them to make progress.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, let's leave this function as is.

@rousskov rousskov added the S-waiting-for-more-reviewers needs a reviewer and/or a second opinion label Nov 12, 2024
@yadij
Copy link
Contributor

yadij commented Nov 13, 2024

Please rebase for easier review. There are changes here from other PRs which were controversial.

@yadij yadij added S-waiting-for-author author action is expected (and usually required) and removed S-waiting-for-more-reviewers needs a reviewer and/or a second opinion labels Nov 13, 2024
Copy link
Contributor

@rousskov rousskov left a comment

Choose a reason for hiding this comment

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

Amos: Please rebase for easier review. There are changes here from other PRs which were controversial.

FWIW, this PR diff did not (and does not) contain any changes from other PRs. It was already based on a recent master, and Eduard has now merged all latest/unrelated master changes into this PR branch, addressing your request.

@@ -783,6 +783,8 @@ commConfigureLinger(const int fd, const OnOff enabled)
l.l_onoff = (enabled == OnOff::on ? 1 : 0);
l.l_linger = 0; // how long to linger for, in seconds

fd_table[fd].flags.harshClosureRequested = (l.l_onoff && !l.l_linger); // close(2) sends TCP RST if true
Copy link
Contributor

@rousskov rousskov Nov 14, 2024

Choose a reason for hiding this comment

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

We would need also to protect setsockopt() by the same check.

Not necessarily: Leaving that system call exposed to caller bugs may be better in this case because it will result in level-0 ERROR message, exposing the bug.

I would just assert(isOpen(fd)) instead.

I would too, but "The following [isOpen(fd) check] fails because ipc.c is doing calls to pipe() to create sockets" comment in _comm_close() worries me. That old comment may no longer reflect what is actually going on, of course, but it is a red flag.

All these complications/problems are outside this PR scope IMO (as detailed at the beginning of this thread). They are best resolved in a dedicated PR. That is why I did not recommend any changes in my review. Said that, if others insist on an if check or assertion, let's add them to make progress.

Comment on lines +882 to 884
if (F->ssl && !F->flags.harshClosureRequested) {
const auto startCall = asyncCall(5, 4, "commStartTlsClose",
callDialer(commStartTlsClose, fd));
Copy link
Contributor

@rousskov rousskov Nov 14, 2024

Choose a reason for hiding this comment

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

Amos: it is not clear that RST is the only result of this function being called. It can and is also called for the FIN cases where TLS should be completed cleanly.

If "this function" is commStartTlsClose():

  • In official code, before this PR: commStartTlsClose() was called in RST and FIN cases, and that was wrong.
  • In PR code, commStartTlsClose() is only called in FIN cases, as intended.
  • RST is not the "result of calling this function".

If "this function" is commConfigureLinger():

  • In official code, before this PR: commConfigureLinger() was called in both RST and FIN cases. That function call had no effect on commStartTlsClose() calls, and that was wrong.
  • In PR code: commConfigureLinger() is called in both RST and FIN cases. That function call bans future commStartTlsClose() calls in RST cases only, as intended.
  • RST is the "result of calling this function" with On parameter, followed by a close(2) system call.

It is, of course, possible that some code paths that end with commStartTlsClose() are missing a commConfigureLinger() call to RST the connection, but that possible problem is outside this PR scope. This PR does not change and should not change when/where commConfigureLinger() is called.

@rousskov rousskov added S-waiting-for-more-reviewers needs a reviewer and/or a second opinion and removed S-waiting-for-author author action is expected (and usually required) labels Nov 14, 2024
@rousskov
Copy link
Contributor

AFAICT, all earlier concerns were addressed on or before November 14. If there are no objections by then, I plan to clear this PR for merging on November 25, 2024.

@squid-anubis squid-anubis added M-waiting-staging-checks https://github.com/measurement-factory/anubis#pull-request-labels M-passed-staging-checks https://github.com/measurement-factory/anubis#pull-request-labels and removed M-waiting-staging-checks https://github.com/measurement-factory/anubis#pull-request-labels M-passed-staging-checks https://github.com/measurement-factory/anubis#pull-request-labels labels Nov 23, 2024
@squid-anubis squid-anubis added M-waiting-staging-checks https://github.com/measurement-factory/anubis#pull-request-labels M-passed-staging-checks https://github.com/measurement-factory/anubis#pull-request-labels and removed M-waiting-staging-checks https://github.com/measurement-factory/anubis#pull-request-labels labels Nov 24, 2024
@rousskov rousskov added M-cleared-for-merge https://github.com/measurement-factory/anubis#pull-request-labels and removed S-waiting-for-more-reviewers needs a reviewer and/or a second opinion labels Nov 26, 2024
@squid-anubis squid-anubis removed the M-passed-staging-checks https://github.com/measurement-factory/anubis#pull-request-labels label Nov 26, 2024
@squid-anubis squid-anubis added M-merged https://github.com/measurement-factory/anubis#pull-request-labels and removed M-cleared-for-merge https://github.com/measurement-factory/anubis#pull-request-labels labels Nov 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
M-merged https://github.com/measurement-factory/anubis#pull-request-labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants