Skip to content
New issue

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

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

Already on GitHub? # to your account

Not retry calling close(...) when EINTR is reported. #217

Merged
merged 1 commit into from
Mar 22, 2018

Conversation

normanmaurer
Copy link
Member

Motivation:

Usually we retry the syscall when EINTR is reported. This is not safe in the case of close(...) as the file descriptor may already be closed. The problem arise if this was the case and we retry it and the file descriptor is already reused again. In this case we would close a wrong file descriptor.

See:

Modifications:

Ignore EINTR on close(...) and just assume the FD was closed.

Result:

Not possible to close the wrong file descriptor when close(...) reports EINTR.

@normanmaurer normanmaurer requested review from weissi and Lukasa March 22, 2018 12:04
@normanmaurer normanmaurer force-pushed the close_eintr branch 2 times, most recently from 71b8ffa to 43fc0e0 Compare March 22, 2018 12:25
@normanmaurer
Copy link
Member Author

Thanks to @vlm for reporting.

@normanmaurer
Copy link
Member Author

@swift-nio-bot test this please

/* Sorry, we really try hard to not use underscored attributes. In this case however we seem to break the inlining threshold which makes a system call take twice the time, ie. we need this exception. */
@inline(__always)
internal func wrapSyscall<T: FixedWidthInteger>(where function: StaticString = #function, _ body: () throws -> T) throws -> T {
internal func wrapSyscall<T: FixedWidthInteger>(where function: StaticString = #function, continueOnErrno: (CInt) -> Bool = continueOnEINTR, _ body: () throws -> T) throws -> T {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we just make this a separate function rather than use the closure?

Copy link
Member Author

Choose a reason for hiding this comment

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

@Lukasa I am not strong on it... that said I think the closure should be free so why not just re-use the rest here ? @weissi WDYT ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit reluctant to combine @inline(__always) with this closure, that's all.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok let me just add an extra method then... Its easy enough to do :)

normanmaurer added a commit to netty/netty that referenced this pull request Mar 22, 2018
Motivation:

If close(...) reports EINTR there is nothing sane we can do so it makes no sense to even report it. See also:

apple/swift-nio#217

Modifications:

Just ignore EINTR when calling close(...)

Result:

Less noise in the logs.
@normanmaurer normanmaurer added this to the 1.3.0 milestone Mar 22, 2018
@normanmaurer normanmaurer added the 🔨 semver/patch No public API change. label Mar 22, 2018
throw IOError(errnoCode: err, function: function)
}
return res
}
Copy link
Contributor

Choose a reason for hiding this comment

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

In this case don't we just want to suppress the return of EINTR altogether? I don't think we need to throw at all.

Copy link
Member Author

Choose a reason for hiding this comment

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

works for me as well...

Copy link
Member

@weissi weissi left a comment

Choose a reason for hiding this comment

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

looks good to me. Are we confident that close just worked if EINTR was returned?

@normanmaurer
Copy link
Member Author

@weissi check the links that are included in the comments... Basically there is really nothing we can do about it and the "safest" bet is to assume it worked.

@normanmaurer
Copy link
Member Author

@swift-nio-bot test this please

Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

This is fine by me. Sadly it's almost impossible to trigger for testing purposes: time to stub out the kernel? 😉

@normanmaurer
Copy link
Member Author

@swift-nio-bot test this please

Motivation:

Usually we retry the syscall when EINTR is reported. This is not safe in the case of close(...) as the file descriptor may already be closed. The problem arise if this was the case and we retry it and the file descriptor is already reused again. In this case we would close a wrong file descriptor.

See:
  - https://bugs.chromium.org/p/chromium/issues/detail?id=269623
  - https://lwn.net/Articles/576478/

Modifications:

Ignore EINTR on close(...) and just assume the FD was closed.

Result:

Not possible to close the wrong file descriptor when close(...) reports EINTR.
@normanmaurer normanmaurer merged commit 91d7058 into apple:master Mar 22, 2018
@normanmaurer normanmaurer deleted the close_eintr branch March 22, 2018 17:16
@vlm
Copy link
Contributor

vlm commented Mar 22, 2018

@Lukasa it should actually be quite possible to trigger it:

  • Create a TCP server that accept a connection with setsockopt(SO_RCVBUF, 4096) but doesn't read anything out of it.
  • Create a TCP client with setsockopt(SO_SNDBUF, 4096) to disable TCP buffers autosizing.
  • Set a non-blocking mode on that client.
  • Attempt to send a lot of data (such as 10⁶ bytes) until EWOULDBLOCK/EAGAIN.
  • Set a blocking mode on a client.
  • Ensure setsockopt(SO_LINGER) has not been tampered with, or reset it to non-zero and say 30 seconds.
  • Set up sigaction(SIGALRM, ~SA_RESTART) (to disable restarting on an alarm).
  • Set an alarm using setitimer().
  • Invoke close() on a client.
  • After alarm goes off the close() should be terminated with -1/EINTR.

I think this should be enough to reliably observe EINTR. If not, the whole thing should be tried with an NFS instead of sockets, but that complicates stuff greatly.

@vlm
Copy link
Contributor

vlm commented Mar 22, 2018

No, turned out under Linux it is pretty impossible to get it this way. Take it back.

normanmaurer added a commit to netty/netty that referenced this pull request Mar 23, 2018
Motivation:

If close(...) reports EINTR there is nothing sane we can do so it makes no sense to even report it. See also:

apple/swift-nio#217

Modifications:

Just ignore EINTR when calling close(...)

Result:

Less noise in the logs.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
🔨 semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants