diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 5ae508e4610..282e5668e6e 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -26,39 +26,35 @@ use libc; target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd"))] -pub const FIONBIO: libc::c_ulong = 0x8004667e; +mod consts { + use libc; + pub const FIONBIO: libc::c_ulong = 0x8004667e; + pub const FIOCLEX: libc::c_ulong = 0x20006601; + pub const FIONCLEX: libc::c_ulong = 0x20006602; +} #[cfg(any(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64")), target_os = "android"))] -pub const FIONBIO: libc::c_ulong = 0x5421; +mod consts { + use libc; + pub const FIONBIO: libc::c_ulong = 0x5421; + pub const FIOCLEX: libc::c_ulong = 0x5451; + pub const FIONCLEX: libc::c_ulong = 0x5450; +} #[cfg(all(target_os = "linux", any(target_arch = "mips", target_arch = "mipsel", target_arch = "powerpc")))] -pub const FIONBIO: libc::c_ulong = 0x667e; - -#[cfg(any(target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", - target_os = "openbsd"))] -pub const FIOCLEX: libc::c_ulong = 0x20006601; -#[cfg(any(all(target_os = "linux", - any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64")), - target_os = "android"))] -pub const FIOCLEX: libc::c_ulong = 0x5451; -#[cfg(all(target_os = "linux", - any(target_arch = "mips", - target_arch = "mipsel", - target_arch = "powerpc")))] -pub const FIOCLEX: libc::c_ulong = 0x6601; +mod consts { + use libc; + pub const FIONBIO: libc::c_ulong = 0x667e; + pub const FIOCLEX: libc::c_ulong = 0x6601; + pub const FIONCLEX: libc::c_ulong = 0x6600; +} +pub use self::consts::*; #[cfg(any(target_os = "macos", target_os = "ios", diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index f7c57c3f5e5..4ef09b91c25 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -13,6 +13,7 @@ use core::prelude::*; use io; use libc::{self, c_int, size_t, c_void}; use mem; +use sys::c; use sys::cvt; use sys_common::AsInner; @@ -51,6 +52,20 @@ impl FileDesc { })); Ok(ret as usize) } + + pub fn set_cloexec(&self) { + unsafe { + let ret = c::ioctl(self.fd, c::FIOCLEX); + debug_assert_eq!(ret, 0); + } + } + + pub fn unset_cloexec(&self) { + unsafe { + let ret = c::ioctl(self.fd, c::FIONCLEX); + debug_assert_eq!(ret, 0); + } + } } impl AsInner for FileDesc { diff --git a/src/libstd/sys/unix/fs2.rs b/src/libstd/sys/unix/fs2.rs index c0426af051b..20b1aac8f45 100644 --- a/src/libstd/sys/unix/fs2.rs +++ b/src/libstd/sys/unix/fs2.rs @@ -215,7 +215,9 @@ impl File { let fd = try!(cvt_r(|| unsafe { libc::open(path.as_ptr(), flags, opts.mode) })); - Ok(File(FileDesc::new(fd))) + let fd = FileDesc::new(fd); + fd.set_cloexec(); + Ok(File(fd)) } pub fn file_attr(&self) -> io::Result { diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 908136a42ab..2e1cbb2a1e1 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -47,7 +47,9 @@ impl Socket { }; unsafe { let fd = try!(cvt(libc::socket(fam, ty, 0))); - Ok(Socket(FileDesc::new(fd))) + let fd = FileDesc::new(fd); + fd.set_cloexec(); + Ok(Socket(fd)) } } @@ -56,13 +58,16 @@ impl Socket { let fd = try!(cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })); - Ok(Socket(FileDesc::new(fd))) + let fd = FileDesc::new(fd); + fd.set_cloexec(); + Ok(Socket(fd)) } pub fn duplicate(&self) -> io::Result { - cvt(unsafe { libc::dup(self.0.raw()) }).map(|fd| { - Socket(FileDesc::new(fd)) - }) + let fd = try!(cvt(unsafe { libc::dup(self.0.raw()) })); + let fd = FileDesc::new(fd); + fd.set_cloexec(); + Ok(Socket(fd)) } pub fn read(&self, buf: &mut [u8]) -> io::Result { diff --git a/src/libstd/sys/unix/pipe2.rs b/src/libstd/sys/unix/pipe2.rs index 7af2c0f0b2a..2fd4d6dd311 100644 --- a/src/libstd/sys/unix/pipe2.rs +++ b/src/libstd/sys/unix/pipe2.rs @@ -32,7 +32,9 @@ pub unsafe fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { impl AnonPipe { pub fn from_fd(fd: libc::c_int) -> AnonPipe { - AnonPipe(FileDesc::new(fd)) + let fd = FileDesc::new(fd); + fd.set_cloexec(); + AnonPipe(fd) } pub fn read(&self, buf: &mut [u8]) -> io::Result { diff --git a/src/test/run-pass/fds-are-cloexec.rs b/src/test/run-pass/fds-are-cloexec.rs new file mode 100644 index 00000000000..ec59b4dc03d --- /dev/null +++ b/src/test/run-pass/fds-are-cloexec.rs @@ -0,0 +1,75 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-windows + +#![feature(libc)] + +extern crate libc; + +use std::env; +use std::fs::{self, File}; +use std::io; +use std::net::{TcpListener, TcpStream, UdpSocket}; +use std::os::unix::prelude::*; +use std::process::Command; +use std::thread; + +fn main() { + let args = env::args().collect::>(); + if args.len() == 1 { + parent() + } else { + child(&args) + } +} + +fn parent() { + let file = File::open("Makefile").unwrap(); + let _dir = fs::read_dir("/").unwrap(); + let tcp1 = TcpListener::bind("127.0.0.1:0").unwrap(); + assert_eq!(tcp1.as_raw_fd(), file.as_raw_fd() + 2); + let tcp2 = tcp1.try_clone().unwrap(); + let addr = tcp1.local_addr().unwrap(); + let t = thread::scoped(|| TcpStream::connect(addr).unwrap()); + let tcp3 = tcp1.accept().unwrap().0; + let tcp4 = t.join(); + let tcp5 = tcp3.try_clone().unwrap(); + let tcp6 = tcp4.try_clone().unwrap(); + let udp1 = UdpSocket::bind("127.0.0.1:0").unwrap(); + let udp2 = udp1.try_clone().unwrap(); + + let status = Command::new(env::args().next().unwrap()) + .arg(file.as_raw_fd().to_string()) + .arg((file.as_raw_fd() + 1).to_string()) + .arg(tcp1.as_raw_fd().to_string()) + .arg(tcp2.as_raw_fd().to_string()) + .arg(tcp3.as_raw_fd().to_string()) + .arg(tcp4.as_raw_fd().to_string()) + .arg(tcp5.as_raw_fd().to_string()) + .arg(tcp6.as_raw_fd().to_string()) + .arg(udp1.as_raw_fd().to_string()) + .arg(udp2.as_raw_fd().to_string()) + .status() + .unwrap(); + assert!(status.success()); +} + +fn child(args: &[String]) { + let mut b = [0u8; 2]; + for arg in &args[1..] { + let fd: libc::c_int = arg.parse().unwrap(); + unsafe { + assert_eq!(libc::read(fd, b.as_mut_ptr() as *mut _, 2), -1); + assert_eq!(io::Error::last_os_error().raw_os_error(), + Some(libc::EBADF)); + } + } +}