Skip to content

Commit

Permalink
sys/unix/process: Reset signal behavior before exec
Browse files Browse the repository at this point in the history
Make sure that child processes don't get affected by libstd's desire to
ignore SIGPIPE, nor a third-party library's signal mask (which is needed
to use either a signal-handling thread correctly or to use signalfd /
kqueue correctly).
  • Loading branch information
geofft committed May 25, 2015
1 parent 195cca7 commit 3617b67
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 deletions.
13 changes: 11 additions & 2 deletions src/libstd/sys/unix/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#![allow(non_camel_case_types)]

pub use self::signal_os::{sigaction, siginfo, sigset_t, sigaltstack};
pub use self::signal_os::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSTKSZ};
pub use self::signal_os::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSTKSZ, SIG_SETMASK};

use libc;

Expand Down Expand Up @@ -111,6 +111,7 @@ pub struct passwd {
pub type sighandler_t = *mut libc::c_void;

pub const SIG_DFL: sighandler_t = 0 as sighandler_t;
pub const SIG_ERR: sighandler_t = !0 as sighandler_t;

extern {
pub fn getsockopt(sockfd: libc::c_int,
Expand All @@ -135,6 +136,8 @@ extern {
oss: *mut sigaltstack) -> libc::c_int;

pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int;
pub fn pthread_sigmask(how: libc::c_int, set: *const sigset_t,
oldset: *mut sigset_t) -> libc::c_int;

#[cfg(not(target_os = "ios"))]
pub fn getpwuid_r(uid: libc::uid_t,
Expand All @@ -155,7 +158,7 @@ extern {
#[cfg(any(target_os = "linux",
target_os = "android"))]
mod signal_os {
pub use self::arch::{SA_ONSTACK, SA_SIGINFO, SIGBUS,
pub use self::arch::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_SETMASK,
sigaction, sigaltstack};
use libc;

Expand Down Expand Up @@ -216,6 +219,8 @@ mod signal_os {

pub const SIGBUS: libc::c_int = 7;

pub const SIG_SETMASK: libc::c_int = 2;

#[cfg(target_os = "linux")]
#[repr(C)]
pub struct sigaction {
Expand Down Expand Up @@ -263,6 +268,8 @@ mod signal_os {

pub const SIGBUS: libc::c_int = 10;

pub const SIG_SETMASK: libc::c_int = 3;

#[cfg(all(target_os = "linux", not(target_env = "musl")))]
#[repr(C)]
pub struct sigaction {
Expand Down Expand Up @@ -321,6 +328,8 @@ mod signal_os {
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub const SIGSTKSZ: libc::size_t = 40960;

pub const SIG_SETMASK: libc::c_int = 3;

#[cfg(any(target_os = "macos",
target_os = "ios"))]
pub type sigset_t = u32;
Expand Down
13 changes: 13 additions & 0 deletions src/libstd/sys/unix/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use ffi::{OsString, OsStr, CString, CStr};
use fmt;
use io::{self, Error, ErrorKind};
use libc::{self, pid_t, c_void, c_int, gid_t, uid_t};
use mem;
use ptr;
use sys::pipe::AnonPipe;
use sys::{self, c, cvt, cvt_r};
Expand Down Expand Up @@ -311,6 +312,18 @@ impl Process {
if !envp.is_null() {
*sys::os::environ() = envp as *const _;
}

// Reset signal handling so the child process starts in a
// standardized state. We ignore SIGPIPE, and signal-handling
// libraries often set a mask; both of these get inherited,
// which most UNIX programs don't expect.
let mut set: c::sigset_t = mem::uninitialized();
if c::sigemptyset(&mut set) != 0 ||
c::pthread_sigmask(c::SIG_SETMASK, &set, ptr::null_mut()) != 0 ||
c::signal(libc::SIGPIPE, c::SIG_DFL) == c::SIG_ERR {
fail(&mut output);
}

let _ = libc::execvp(*argv, argv as *mut _);
fail(&mut output)
}
Expand Down
36 changes: 36 additions & 0 deletions src/test/run-pass/process-sigpipe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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.

// libstd ignores SIGPIPE, and other libraries may set signal masks.
// Make sure that these behaviors don't get inherited to children
// spawned via std::process, since they're needed for traditional UNIX
// filter behavior. This test checks that `yes | head` terminates
// (instead of running forever), and that it does not print an error
// message about a broken pipe.

use std::process;
use std::thread;

#[cfg(unix)]
fn main() {
// Just in case `yes` doesn't check for EPIPE...
thread::spawn(|| {
thread::sleep_ms(5000);
process::exit(1);
});
let output = process::Command::new("sh").arg("-c").arg("yes | head").output().unwrap();
assert!(output.status.success());
assert!(output.stderr.len() == 0);
}

#[cfg(not(unix))]
fn main() {
// Not worried about signal masks on other platforms
}

0 comments on commit 3617b67

Please sign in to comment.