Skip to content

Commit 3617b67

Browse files
committed
sys/unix/process: Reset signal behavior before exec
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).
1 parent 195cca7 commit 3617b67

File tree

3 files changed

+60
-2
lines changed

3 files changed

+60
-2
lines changed

src/libstd/sys/unix/c.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#![allow(non_camel_case_types)]
2525

2626
pub use self::signal_os::{sigaction, siginfo, sigset_t, sigaltstack};
27-
pub use self::signal_os::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSTKSZ};
27+
pub use self::signal_os::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSTKSZ, SIG_SETMASK};
2828

2929
use libc;
3030

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

113113
pub const SIG_DFL: sighandler_t = 0 as sighandler_t;
114+
pub const SIG_ERR: sighandler_t = !0 as sighandler_t;
114115

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

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

139142
#[cfg(not(target_os = "ios"))]
140143
pub fn getpwuid_r(uid: libc::uid_t,
@@ -155,7 +158,7 @@ extern {
155158
#[cfg(any(target_os = "linux",
156159
target_os = "android"))]
157160
mod signal_os {
158-
pub use self::arch::{SA_ONSTACK, SA_SIGINFO, SIGBUS,
161+
pub use self::arch::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_SETMASK,
159162
sigaction, sigaltstack};
160163
use libc;
161164

@@ -216,6 +219,8 @@ mod signal_os {
216219

217220
pub const SIGBUS: libc::c_int = 7;
218221

222+
pub const SIG_SETMASK: libc::c_int = 2;
223+
219224
#[cfg(target_os = "linux")]
220225
#[repr(C)]
221226
pub struct sigaction {
@@ -263,6 +268,8 @@ mod signal_os {
263268

264269
pub const SIGBUS: libc::c_int = 10;
265270

271+
pub const SIG_SETMASK: libc::c_int = 3;
272+
266273
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
267274
#[repr(C)]
268275
pub struct sigaction {
@@ -321,6 +328,8 @@ mod signal_os {
321328
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
322329
pub const SIGSTKSZ: libc::size_t = 40960;
323330

331+
pub const SIG_SETMASK: libc::c_int = 3;
332+
324333
#[cfg(any(target_os = "macos",
325334
target_os = "ios"))]
326335
pub type sigset_t = u32;

src/libstd/sys/unix/process.rs

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use ffi::{OsString, OsStr, CString, CStr};
1717
use fmt;
1818
use io::{self, Error, ErrorKind};
1919
use libc::{self, pid_t, c_void, c_int, gid_t, uid_t};
20+
use mem;
2021
use ptr;
2122
use sys::pipe::AnonPipe;
2223
use sys::{self, c, cvt, cvt_r};
@@ -311,6 +312,18 @@ impl Process {
311312
if !envp.is_null() {
312313
*sys::os::environ() = envp as *const _;
313314
}
315+
316+
// Reset signal handling so the child process starts in a
317+
// standardized state. We ignore SIGPIPE, and signal-handling
318+
// libraries often set a mask; both of these get inherited,
319+
// which most UNIX programs don't expect.
320+
let mut set: c::sigset_t = mem::uninitialized();
321+
if c::sigemptyset(&mut set) != 0 ||
322+
c::pthread_sigmask(c::SIG_SETMASK, &set, ptr::null_mut()) != 0 ||
323+
c::signal(libc::SIGPIPE, c::SIG_DFL) == c::SIG_ERR {
324+
fail(&mut output);
325+
}
326+
314327
let _ = libc::execvp(*argv, argv as *mut _);
315328
fail(&mut output)
316329
}

src/test/run-pass/process-sigpipe.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// libstd ignores SIGPIPE, and other libraries may set signal masks.
12+
// Make sure that these behaviors don't get inherited to children
13+
// spawned via std::process, since they're needed for traditional UNIX
14+
// filter behavior. This test checks that `yes | head` terminates
15+
// (instead of running forever), and that it does not print an error
16+
// message about a broken pipe.
17+
18+
use std::process;
19+
use std::thread;
20+
21+
#[cfg(unix)]
22+
fn main() {
23+
// Just in case `yes` doesn't check for EPIPE...
24+
thread::spawn(|| {
25+
thread::sleep_ms(5000);
26+
process::exit(1);
27+
});
28+
let output = process::Command::new("sh").arg("-c").arg("yes | head").output().unwrap();
29+
assert!(output.status.success());
30+
assert!(output.stderr.len() == 0);
31+
}
32+
33+
#[cfg(not(unix))]
34+
fn main() {
35+
// Not worried about signal masks on other platforms
36+
}

0 commit comments

Comments
 (0)