提交 d6c72306 编写于 作者: A Alex Crichton

std: Set CLOEXEC for all fds opened on unix

This commit starts to set the CLOEXEC flag for all files and sockets opened by
the standard library by default on all unix platforms. There are a few points of
note in this commit:

* The implementation is not 100% satisfactory in the face of threads. File
  descriptors only have the `F_CLOEXEC` flag set *after* they are opened,
  allowing for a fork/exec to happen in the middle and leak the descriptor.
  Some platforms do support atomically opening a descriptor while setting the
  `CLOEXEC` flag, and it is left as a future extension to bind these apis as it
  is unclear how to do so nicely at this time.

* The implementation does not offer a method of opting into the old behavior of
  not setting `CLOEXEC`. This will possibly be added in the future through
  extensions on `OpenOptions`, for example.

* This change does not yet audit any Windows APIs to see if the handles are
  inherited by default by accident.

This is a breaking change for users who call `fork` or `exec` outside of the
standard library itself and expect file descriptors to be inherted. All file
descriptors created by the standard library will no longer be inherited.

[breaking-change]
上级 88fc5438
......@@ -26,39 +26,35 @@
target_os = "dragonfly",
target_os = "bitrig",
target_os = "openbsd"))]
pub const FIONBIO: libc::c_ulong = 0x8004667e;
#[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;
#[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;
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 FIOCLEX: libc::c_ulong = 0x5451;
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 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",
......
......@@ -13,6 +13,7 @@
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 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
}));
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<c_int> for FileDesc {
......
......@@ -215,7 +215,9 @@ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<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<FileAttr> {
......
......@@ -47,7 +47,9 @@ pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<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 @@ pub fn accept(&self, storage: *mut libc::sockaddr,
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<Socket> {
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<usize> {
......
......@@ -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<usize> {
......
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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::<Vec<_>>();
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));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册