-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Atomically set CLOEXEC on duplicated sockets #27980
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
@barosl Could you provide the constants for the different OSs again, please? |
#[cfg(target_os = "freebsd")] | ||
pub mod posix08 { | ||
use types::os::arch::c95::c_int; | ||
pub const O_CLOEXEC: c_int = 0x100000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub const F_DUPFD_CLOEXEC : c_int = 17;
@nagisa May I ask how you get all these constants? Do you have virtual machines? Do you look them up in the source code? |
Is it necessary to base this PR onto #27971 instead of master? |
@nagisa Yes, otherwise merge conflicts would occur around the |
Can this also avoid copying the constant definition in liblibc? This probably is not the only mistake in the organization of liblibc, so it's better to reduce the duplication than try to keep it correct from where it is today. |
@alexcrichton Should the new constants be put in the incorrect module as well (for consistency) or in the |
@@ -66,7 +67,30 @@ impl Socket { | |||
} | |||
|
|||
pub fn duplicate(&self) -> io::Result<Socket> { | |||
let fd = try!(cvt(unsafe { libc::dup(self.0.raw()) })); | |||
use libc::funcs::posix88::fcntl::fcntl; | |||
const ZERO: c_int = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How come this and MINUS_ONE
are constants? Could they just be literals and/or locals?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why I added MINUS_ONE
, and will remove that, but ZERO
is kind of required to make sure the type of 0
is c_int
as this is passed as varargs to fcntl
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does anything go wrong if it's just passed as a function argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't guarantee it's c_int
then, because the compiler doesn't (/can't) know the type of the third parameter. Whether anything goes wrong or not, one would need to have better understanding of this situation than I do because this is unsafe code.
(For reference, the function signature is declared as int fcntl(int filedes, int cmd, ...)
, and in our case int fcntl(int filedes, F_DUPFD_CLOEXEC, int arg)
.)
So long as they find their way into either liblibc or |
} | ||
}); | ||
unsafe { FCNTL_DUPFD } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment explaining what this function is doing? I'm also somewhat wary about the assert!
here as there's probably some random kernel which returns some obscure error code. Are we also guaranteed that EINVAL
is never returned for a bad file descriptor?
I'm not sure if there's some prior art to draw from in this sort of detection, but I'm a little wary of this...
Here you are! Linux: NetBSD's seems to differ from the current value of Tested on my VMs. |
@alexcrichton Added a comment and referenced prior art: http://comments.gmane.org/gmane.linux.lib.musl.general/2963. |
@@ -4632,7 +4646,7 @@ pub mod consts { | |||
pub const F_GETLK : c_int = 7; | |||
pub const F_SETLK : c_int = 8; | |||
pub const F_SETLKW : c_int = 9; | |||
pub const F_DUPFD_CLOEXEC : c_int = 10; | |||
pub use consts::os::posix08::F_DUPFD_CLOEXEC; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, #27930 already introduced a breaking change to libc
, which changed the location of _PC_NAME_MAX
from the funcs
module (!) to the consts
module. So personally, I think it would be clearer if this got removed completely, as we're committing breakage anyways. But YMMV!
Hm it looks like the implementation here is pretty different from that inside MUSL. The mailing list thread indicates that there are two conditions we can get EINVAL:
It's probably tough to say where EBADF vs EINVAL is returned, and that thread doesn't seem to have a conclusion about whether EINVAL could perhaps be returned for bad file descriptors. How about switching up the implementation to the just-as-efficient (on newer systems at least) but simpler (and closer to what MUSL does): pub fn duplicate(&self) -> io::Result<Socket> {
cvt(unsafe {
libc::fcntl(self.0.raw(), libc::FCNTL_DUPFD_CLOEXEC, 0)
}).or_else(|e| {
if e.raw_os_error() == Some(libc::EINVAL) {
cvt(unsafe { libc::fcntl(self.0.raw(), libc::FCNTL_DUPFD, 0) })
} else {
Err(e)
}
}).map(|fd| {
let fd = FileDesc::new();
fd.set_cloexec();
Socket(fd)
})
} We know that the first case of EINVAL can't be returned because we have a valid file descriptor so that's all we need to check for. It's also not clear to me whether this is worth the current platform-specific logic plus the caching via |
@alexcrichton The documentation of the syscall and the linked mailing list thread agree on that I'll try to get closer to MUSL though... |
@alexcrichton It's now nearer to the MUSL code. |
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => { | ||
EMULATE_F_DUPFD_CLOEXEC.store(true, atomic::Ordering::Relaxed); | ||
} | ||
res => return res.map(|fd| Socket(FileDesc::new(fd))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An interesting point of the MUSL thread indicated that some newer kernels apparently broke F_DUPFD_CLOEXEC
such that it was the same as F_DUPFD
, and hence even if this syscall succeeds they still set cloexec. It may be good to just keep the same common code at the end where set_cloexec
is called?
@alexcrichton Done. |
Looks good to me, thanks! r=me with a squash and a rebase |
For Bitrig, NetBSD and OpenBSD the constant was incorrectly in posix01, when it's actually posix08, so we move it. This is a [breaking-change], but we already had one in rust-lang#27930. Fix NetBSD's F_DUPFD_CLOEXEC constant. For a similar feature detection, see this musl thread: http://comments.gmane.org/gmane.linux.lib.musl.general/2963 This assumes that an int literal has type `c_int` for varidic functions.
@alexcrichton Rebased. |
Still needs values of F_DUPFD_CLOEXEC on other OSs. For Bitrig, NetBSD and OpenBSD the constant was incorrectly in posix01, when it's actually posix08. In order to maintain backwards-compatiblity, the constant was only copied, not moved. cc #24237
These commits finish up closing out #24237 by filling out all locations we create new file descriptors with variants that atomically create the file descriptor and set CLOEXEC where possible. Previous support for doing this in `File::open` was added in #27971 and support for `try_clone` was added in #27980. This commit fills out: * `Socket::new` now passes `SOCK_CLOEXEC` * `Socket::accept` now uses `accept4` * `pipe2` is used instead of `pipe` Unfortunately most of this support is Linux-specific, and most of it is post-2.6.18 (our oldest supported version), so all of the detection here is done dynamically. It looks like OSX does not have equivalent variants for these functions, so there's nothing more we can do there. Support for BSDs can be added over time if they also have these functions. Closes #24237
Still needs values of F_DUPFD_CLOEXEC on other OSs.
For Bitrig, NetBSD and OpenBSD the constant was incorrectly in posix01, when
it's actually posix08. In order to maintain backwards-compatiblity, the
constant was only copied, not moved.
cc #24237