Change:
Good question! There are 6 workarounds in for older Linux versions (that I could find). Increasing the minimum version to 2.6.32 (aka 3rd Kernel LTS, aka RHEL 6) would fix 5 of them. Code links are inline: uses the flag with  (mentioned above, in 2.6.23) uses with  ( in 2.6.24, there is also the mention of a bug occuring on "some linux kernel at some point") uses  to atomically set the flag on the pipe fds ( in 2.6.27) uses with  ( in 2.6.27) uses  to permit use of ( in 2.6.28) ~ ( in 4.5, not fixed by this proposal)~ As you can see, the workarounds fixed by this proposal all have a similar flavor. Originally posted by in
Kernel updated in .
As noted, needs a much newer kernel 4.5, so we'll still need that fallback. But all the stuff should be ok to assume available now.
The new minimal version is 2.6.27 from what I can see, so "Socket::accept uses accept4(2) to permit use of SOCK_CLOEXEC" still needs a fallback, right?
The builder is using 2.6.32 headers, so I would call that our new minimum.
Ah right; I am actually not sure where I got the 27 from...
The title of suggested 2.6.27, but we went a little higher to match the actual distros in use.
I know this is ancient history, but do you recall the problem with ? You introduced that comment about "some kernel" in , and you were the last to update it in . If the problem kernels were before 2.6.32, then I'd like to clean that up now.
Just digging in kernel history, I did find one hiccup in , but that bug was introduced and resolved during the 3.7 development cycle, not in a release.
IIRC we just mirrored what musl did at the time, which looks like it : Looks like the original mailing list thread is gone and there's no comments in the musl source, and I don't recall what happened here. I suspect it would be safe to remove those workarounds and just assume works one the first try until someone says differently.

Discussion:
use crate::cmp; use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; use crate::mem; <del> use crate::sync::atomic::{AtomicBool, Ordering}; </del> use crate::sys::cvt; use crate::sys_common::AsInner;

pub fn duplicate(&self) -> io::Result<FileDesc> { // We want to atomically duplicate this file descriptor and set the // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This <del> // flag, however, isn't supported on older Linux kernels (earlier than // 2.6.24). // // To detect this and ensure that CLOEXEC is still set, we // follow a strategy similar to musl [1] where if passing // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not // supported (the third parameter, 0, is always valid), so we stop // trying that. // // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to // resolve so we at least compile this. // // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963 #[cfg(any(target_os = "android", target_os = "haiku"))] use libc::F_DUPFD as F_DUPFD_CLOEXEC; #[cfg(not(any(target_os = "android", target_os = "haiku")))] use libc::F_DUPFD_CLOEXEC; let make_filedesc = |fd| { let fd = FileDesc::new(fd); fd.set_cloexec()?; Ok(fd) }; static TRY_CLOEXEC: AtomicBool = AtomicBool::new(!cfg!(target_os = "android")); let fd = self.raw(); if TRY_CLOEXEC.load(Ordering::Relaxed) { match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) { // We *still* call the `set_cloexec` method as apparently some // linux kernel at some point stopped setting CLOEXEC even // though it reported doing so on F_DUPFD_CLOEXEC. Ok(fd) => { return Ok(if cfg!(target_os = "linux") { make_filedesc(fd)? } else { FileDesc::new(fd) }); } Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => { TRY_CLOEXEC.store(false, Ordering::Relaxed); } Err(e) => return Err(e), } } cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc) </del> <ins> // is a POSIX flag that was added to Linux in 2.6.24. let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?; Ok(FileDesc::new(fd)) </ins> } }

// However, since this is a variadic function, C integer promotion rules mean that on // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms). let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; <del> let fd = FileDesc::new(fd); // Currently the standard library supports Linux 2.6.18 which did not // have the O_CLOEXEC flag (passed above). If we're running on an older // Linux kernel then the flag is just ignored by the OS. After we open // the first file, we check whether it has CLOEXEC set. If it doesn't, // we will explicitly ask for a CLOEXEC fd for every further file we // open, if it does, we will skip that step. // // The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc // that we support, so we only do this on Linux currently. #[cfg(target_os = "linux")] fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> { use crate::sync::atomic::{AtomicUsize, Ordering}; const OPEN_CLOEXEC_UNKNOWN: usize = 0; const OPEN_CLOEXEC_SUPPORTED: usize = 1; const OPEN_CLOEXEC_NOTSUPPORTED: usize = 2; static OPEN_CLOEXEC: AtomicUsize = AtomicUsize::new(OPEN_CLOEXEC_UNKNOWN); let need_to_set; match OPEN_CLOEXEC.load(Ordering::Relaxed) { OPEN_CLOEXEC_UNKNOWN => { need_to_set = !fd.get_cloexec()?; OPEN_CLOEXEC.store( if need_to_set { OPEN_CLOEXEC_NOTSUPPORTED } else { OPEN_CLOEXEC_SUPPORTED }, Ordering::Relaxed, ); } OPEN_CLOEXEC_SUPPORTED => need_to_set = false, OPEN_CLOEXEC_NOTSUPPORTED => need_to_set = true, _ => unreachable!(), } if need_to_set { fd.set_cloexec()?; } Ok(()) } #[cfg(not(target_os = "linux"))] fn ensure_cloexec(_: &FileDesc) -> io::Result<()> { Ok(()) } ensure_cloexec(&fd)?; Ok(File(fd)) </del> <ins> Ok(File(FileDesc::new(fd))) </ins> } pub fn file_attr(&self) -> io::Result<FileAttr> {

pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { unsafe { <del> // On linux we first attempt to pass the SOCK_CLOEXEC flag to // atomically create the socket and set it as CLOEXEC. Support for // this option, however, was added in 2.6.27, and we still support // 2.6.18 as a kernel, so if the returned error is EINVAL we // fallthrough to the fallback. #[cfg(target_os = "linux")] { match cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0)) { Ok(fd) => return Ok(Socket(FileDesc::new(fd))), Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {} Err(e) => return Err(e), } } let fd = cvt(libc::socket(fam, ty, 0))?; let fd = FileDesc::new(fd); fd.set_cloexec()?; let socket = Socket(fd); </del> <ins> cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { // On Linux we pass the SOCK_CLOEXEC flag to atomically create // the socket and set it as CLOEXEC, added in 2.6.27. let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; Ok(Socket(FileDesc::new(fd))) } else { let fd = cvt(libc::socket(fam, ty, 0))?; let fd = FileDesc::new(fd); fd.set_cloexec()?; let socket = Socket(fd); </ins> <del> // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` // flag to disable `SIGPIPE` emission on socket. #[cfg(target_vendor = "apple")] setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; </del> <ins> // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` // flag to disable `SIGPIPE` emission on socket. #[cfg(target_vendor = "apple")] setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; </ins> <del> Ok(socket) </del> <ins> Ok(socket) } } </ins> } }

unsafe { let mut fds = [0, 0]; <del> // Like above, see if we can set cloexec atomically #[cfg(target_os = "linux")] { match cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr())) { Ok(_) => { return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))); } Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {} Err(e) => return Err(e), </del> <ins> cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))) } else { cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; let a = FileDesc::new(fds[0]); let b = FileDesc::new(fds[1]); a.set_cloexec()?; b.set_cloexec()?; Ok((Socket(a), Socket(b))) </ins> } } <del> cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; let a = FileDesc::new(fds[0]); let b = FileDesc::new(fds[1]); a.set_cloexec()?; b.set_cloexec()?; Ok((Socket(a), Socket(b))) </del> } }

pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> { // Unfortunately the only known way right now to accept a socket and // atomically set the CLOEXEC flag is to use the `accept4` syscall on <del> // Linux. This was added in 2.6.28, however, and because we support // 2.6.18 we must detect this support dynamically. #[cfg(target_os = "linux")] { syscall! { fn accept4( fd: c_int, addr: *mut sockaddr, addr_len: *mut socklen_t, flags: c_int ) -> c_int } let res = cvt_r(|| unsafe { accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) }); match res { Ok(fd) => return Ok(Socket(FileDesc::new(fd))), Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} Err(e) => return Err(e), </del> <ins> // Linux. This was added in 2.6.28, glibc 2.10 and musl 0.9.5. cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { let fd = cvt_r(|| unsafe { libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) })?; Ok(Socket(FileDesc::new(fd))) } else { let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; let fd = FileDesc::new(fd); fd.set_cloexec()?; Ok(Socket(fd)) </ins> } } <del> let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; let fd = FileDesc::new(fd); fd.set_cloexec()?; Ok(Socket(fd)) </del> } pub fn duplicate(&self) -> io::Result<Socket> {

/// Sets the platform-specific value of errno #[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly")))] // needed for readdir and syscall! <ins> #[allow(dead_code)] // but not all target cfgs actually end up using it </ins> pub fn set_errno(e: i32) { unsafe { *errno_location() = e as c_int } }

use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; <del> use crate::sync::atomic::{AtomicBool, Ordering}; </del> use crate::sys::fd::FileDesc; use crate::sys::{cvt, cvt_r}; <del> use libc::c_int; </del> //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes ////////////////////////////////////////////////////////////////////////////////

pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { <del> syscall! { fn pipe2(fds: *mut c_int, flags: c_int) -> c_int } static INVALID: AtomicBool = AtomicBool::new(false); </del> let mut fds = [0; 2]; <del> // Unfortunately the only known way right now to create atomically set the // CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in // 2.6.27, however, and because we support 2.6.18 we must detect this // support dynamically. if cfg!(any( target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", target_os = "redox" )) && !INVALID.load(Ordering::SeqCst) { // Note that despite calling a glibc function here we may still // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to // emulate on older kernels, so if you happen to be running on // an older kernel you may see `pipe2` as a symbol but still not // see the syscall. match cvt(unsafe { pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }) { Ok(_) => { return Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))); } Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => { INVALID.store(true, Ordering::SeqCst); } Err(e) => return Err(e), </del> <ins> // The only known way right now to create atomically set the CLOEXEC flag is // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 // and musl 0.9.3, and some other targets also have it. cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", target_os = "redox" ))] { cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?; Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))) } else { cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; let fd0 = FileDesc::new(fds[0]); let fd1 = FileDesc::new(fds[1]); fd0.set_cloexec()?; fd1.set_cloexec()?; Ok((AnonPipe(fd0), AnonPipe(fd1))) </ins> } } <del> cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; let fd0 = FileDesc::new(fds[0]); let fd1 = FileDesc::new(fds[1]); fd0.set_cloexec()?; fd1.set_cloexec()?; Ok((AnonPipe(fd0), AnonPipe(fd1))) </del> } impl AnonPipe {

//! symbol, but that caused Debian to detect an unnecessarily strict versioned //! dependency on libc6 (#23628). <ins> // There are a variety of `#[cfg]`s controlling which targets are involved in // each instance of `weak!` and `syscall!`. Rather than trying to unify all of // that, we'll just allow that some unix targets don't use this module at all. #![allow(dead_code, unused_macros)] </ins> use crate::ffi::CStr; use crate::marker; use crate::mem;