diff --git a/src/poll.rs b/src/poll.rs index 1a009c3e..603ff4d4 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -263,6 +263,13 @@ impl Poll { /// the system selector. If this syscall fails, `Poll::new` will return /// with the error. /// + /// close-on-exec flag is set on the file descriptors used by the selector to prevent + /// leaking it to executed processes. However, on some systems such as + /// old Linux systems that don't support `epoll_create1` syscall it is done + /// non-atomically, so a separate thread executing in parallel to this + /// function may accidentally leak the file descriptor if it executes a + /// new process before this function returns. + /// /// See [struct] level docs for more details. /// /// [struct]: struct.Poll.html diff --git a/src/sys/unix/selector/epoll.rs b/src/sys/unix/selector/epoll.rs index 7853ed65..477642a1 100644 --- a/src/sys/unix/selector/epoll.rs +++ b/src/sys/unix/selector/epoll.rs @@ -23,15 +23,8 @@ pub struct Selector { impl Selector { pub fn new() -> io::Result { - // According to libuv, `EPOLL_CLOEXEC` is not defined on Android API < - // 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform, - // so we use it instead. - #[cfg(target_os = "android")] - let flag = libc::O_CLOEXEC; - #[cfg(not(target_os = "android"))] - let flag = libc::EPOLL_CLOEXEC; - - syscall!(epoll_create1(flag)).map(|ep| Selector { + let ep = new_epoll_fd()?; + Ok(Selector { #[cfg(debug_assertions)] id: NEXT_ID.fetch_add(1, Ordering::Relaxed), ep, @@ -133,6 +126,43 @@ impl Drop for Selector { } } +/// Creates a new epoll file descriptor with close-on-exec flag set. +/// +/// close-on-exec is set atomically with `epoll_create1()` if possible, +/// otherwise `epoll_create()` and `fcntl()` calls are used as a fallback. +fn new_epoll_fd() -> io::Result { + // According to libuv, `EPOLL_CLOEXEC` is not defined on Android API < + // 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform, + // so we use it instead. + #[cfg(target_os = "android")] + let flag = libc::O_CLOEXEC; + #[cfg(not(target_os = "android"))] + let flag = libc::EPOLL_CLOEXEC; + + #[cfg(not(target_os = "android"))] + let ep = syscall!(epoll_create1(flag))?; + + // On Android try to use epoll_create1 syscall with an epoll_create fallback + // to support Android API level 16 which does not define epoll_create1 function. + #[cfg(target_os = "android")] + let ep = syscall!(syscall(libc::SYS_epoll_create1, flag)) + .map(|fd| fd as libc::c_int) + .or_else(|e| { + match e.raw_os_error() { + Some(libc::ENOSYS) => { + // Using epoll_create() followed by fcntl() instead of epoll_create1() with EPOLL_CLOEXEC + // flag for backwards compatibility. + let ep = syscall!(epoll_create(1024))?; + + syscall!(fcntl(ep, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| ep) + } + _ => Err(e), + } + })?; + + Ok(ep) +} + fn interests_to_epoll(interests: Interest) -> u32 { let mut kind = EPOLLET;