1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2019 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker
5*bb4ee6a4SAndroid Build Coastguard Worker use std::env;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::io::stderr;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Read;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::panic;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::panic::PanicInfo;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::process::abort;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::string::String;
13*bb4ee6a4SAndroid Build Coastguard Worker
14*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
15*bb4ee6a4SAndroid Build Coastguard Worker use base::FromRawDescriptor;
16*bb4ee6a4SAndroid Build Coastguard Worker use base::IntoRawDescriptor;
17*bb4ee6a4SAndroid Build Coastguard Worker use libc::close;
18*bb4ee6a4SAndroid Build Coastguard Worker use libc::dup;
19*bb4ee6a4SAndroid Build Coastguard Worker use libc::dup2;
20*bb4ee6a4SAndroid Build Coastguard Worker use libc::pipe2;
21*bb4ee6a4SAndroid Build Coastguard Worker use libc::O_NONBLOCK;
22*bb4ee6a4SAndroid Build Coastguard Worker use libc::STDERR_FILENO;
23*bb4ee6a4SAndroid Build Coastguard Worker
24*bb4ee6a4SAndroid Build Coastguard Worker // Opens a pipe and puts the write end into the stderr FD slot. On success, returns the read end of
25*bb4ee6a4SAndroid Build Coastguard Worker // the pipe and the old stderr as a pair of files.
26*bb4ee6a4SAndroid Build Coastguard Worker //
27*bb4ee6a4SAndroid Build Coastguard Worker // This may fail if, for example, the file descriptor numbers have been exhausted.
redirect_stderr() -> Option<(File, File)>28*bb4ee6a4SAndroid Build Coastguard Worker fn redirect_stderr() -> Option<(File, File)> {
29*bb4ee6a4SAndroid Build Coastguard Worker let mut fds = [-1, -1];
30*bb4ee6a4SAndroid Build Coastguard Worker // SAFETY: Trivially safe because the return value is checked.
31*bb4ee6a4SAndroid Build Coastguard Worker unsafe {
32*bb4ee6a4SAndroid Build Coastguard Worker let old_stderr = dup(STDERR_FILENO);
33*bb4ee6a4SAndroid Build Coastguard Worker if old_stderr == -1 {
34*bb4ee6a4SAndroid Build Coastguard Worker return None;
35*bb4ee6a4SAndroid Build Coastguard Worker }
36*bb4ee6a4SAndroid Build Coastguard Worker // Safe because pipe2 will only ever write two integers to our array and we check output.
37*bb4ee6a4SAndroid Build Coastguard Worker let mut ret = pipe2(fds.as_mut_ptr(), O_NONBLOCK);
38*bb4ee6a4SAndroid Build Coastguard Worker if ret != 0 {
39*bb4ee6a4SAndroid Build Coastguard Worker // Leaks FDs, but not important right before abort.
40*bb4ee6a4SAndroid Build Coastguard Worker return None;
41*bb4ee6a4SAndroid Build Coastguard Worker }
42*bb4ee6a4SAndroid Build Coastguard Worker // Safe because the FD we are duplicating is owned by us.
43*bb4ee6a4SAndroid Build Coastguard Worker ret = dup2(fds[1], STDERR_FILENO);
44*bb4ee6a4SAndroid Build Coastguard Worker if ret == -1 {
45*bb4ee6a4SAndroid Build Coastguard Worker // Leaks FDs, but not important right before abort.
46*bb4ee6a4SAndroid Build Coastguard Worker return None;
47*bb4ee6a4SAndroid Build Coastguard Worker }
48*bb4ee6a4SAndroid Build Coastguard Worker // The write end is no longer needed.
49*bb4ee6a4SAndroid Build Coastguard Worker close(fds[1]);
50*bb4ee6a4SAndroid Build Coastguard Worker // Safe because each of the fds was the result of a successful FD creation syscall.
51*bb4ee6a4SAndroid Build Coastguard Worker Some((
52*bb4ee6a4SAndroid Build Coastguard Worker File::from_raw_descriptor(fds[0]),
53*bb4ee6a4SAndroid Build Coastguard Worker File::from_raw_descriptor(old_stderr),
54*bb4ee6a4SAndroid Build Coastguard Worker ))
55*bb4ee6a4SAndroid Build Coastguard Worker }
56*bb4ee6a4SAndroid Build Coastguard Worker }
57*bb4ee6a4SAndroid Build Coastguard Worker
58*bb4ee6a4SAndroid Build Coastguard Worker // Sets stderr to the given file. Returns true on success.
restore_stderr(stderr: File) -> bool59*bb4ee6a4SAndroid Build Coastguard Worker fn restore_stderr(stderr: File) -> bool {
60*bb4ee6a4SAndroid Build Coastguard Worker let descriptor = stderr.into_raw_descriptor();
61*bb4ee6a4SAndroid Build Coastguard Worker
62*bb4ee6a4SAndroid Build Coastguard Worker // SAFETY:
63*bb4ee6a4SAndroid Build Coastguard Worker // Safe because descriptor is guaranteed to be valid and replacing stderr
64*bb4ee6a4SAndroid Build Coastguard Worker // should be an atomic operation.
65*bb4ee6a4SAndroid Build Coastguard Worker unsafe { dup2(descriptor, STDERR_FILENO) != -1 }
66*bb4ee6a4SAndroid Build Coastguard Worker }
67*bb4ee6a4SAndroid Build Coastguard Worker
68*bb4ee6a4SAndroid Build Coastguard Worker // Sends as much information about the panic as possible to syslog.
log_panic_info(default_panic: &(dyn Fn(&PanicInfo) + Sync + Send + 'static), info: &PanicInfo)69*bb4ee6a4SAndroid Build Coastguard Worker fn log_panic_info(default_panic: &(dyn Fn(&PanicInfo) + Sync + Send + 'static), info: &PanicInfo) {
70*bb4ee6a4SAndroid Build Coastguard Worker // Grab a lock of stderr to prevent concurrent threads from trampling on our stderr capturing
71*bb4ee6a4SAndroid Build Coastguard Worker // procedure. The default_panic procedure likely uses stderr.lock as well, but the mutex inside
72*bb4ee6a4SAndroid Build Coastguard Worker // stderr is reentrant, so it will not dead-lock on this thread.
73*bb4ee6a4SAndroid Build Coastguard Worker let stderr = stderr();
74*bb4ee6a4SAndroid Build Coastguard Worker let _stderr_lock = stderr.lock();
75*bb4ee6a4SAndroid Build Coastguard Worker
76*bb4ee6a4SAndroid Build Coastguard Worker // Redirect stderr to a pipe we can read from later.
77*bb4ee6a4SAndroid Build Coastguard Worker let (mut read_file, old_stderr) = match redirect_stderr() {
78*bb4ee6a4SAndroid Build Coastguard Worker Some(f) => f,
79*bb4ee6a4SAndroid Build Coastguard Worker None => {
80*bb4ee6a4SAndroid Build Coastguard Worker error!(
81*bb4ee6a4SAndroid Build Coastguard Worker "failed to capture stderr during panic: {}",
82*bb4ee6a4SAndroid Build Coastguard Worker std::io::Error::last_os_error()
83*bb4ee6a4SAndroid Build Coastguard Worker );
84*bb4ee6a4SAndroid Build Coastguard Worker // Fallback to stderr only panic logging.
85*bb4ee6a4SAndroid Build Coastguard Worker env::set_var("RUST_BACKTRACE", "1");
86*bb4ee6a4SAndroid Build Coastguard Worker default_panic(info);
87*bb4ee6a4SAndroid Build Coastguard Worker return;
88*bb4ee6a4SAndroid Build Coastguard Worker }
89*bb4ee6a4SAndroid Build Coastguard Worker };
90*bb4ee6a4SAndroid Build Coastguard Worker // Only through the default panic handler can we get a stacktrace. It only ever prints to
91*bb4ee6a4SAndroid Build Coastguard Worker // stderr, hence all the previous code to redirect it to a pipe we can read.
92*bb4ee6a4SAndroid Build Coastguard Worker env::set_var("RUST_BACKTRACE", "1");
93*bb4ee6a4SAndroid Build Coastguard Worker default_panic(info);
94*bb4ee6a4SAndroid Build Coastguard Worker
95*bb4ee6a4SAndroid Build Coastguard Worker // Closes the write end of the pipe so that we can reach EOF in read_to_string. Also allows
96*bb4ee6a4SAndroid Build Coastguard Worker // others to write to stderr without failure.
97*bb4ee6a4SAndroid Build Coastguard Worker if !restore_stderr(old_stderr) {
98*bb4ee6a4SAndroid Build Coastguard Worker error!("failed to restore stderr during panic");
99*bb4ee6a4SAndroid Build Coastguard Worker return;
100*bb4ee6a4SAndroid Build Coastguard Worker }
101*bb4ee6a4SAndroid Build Coastguard Worker drop(_stderr_lock);
102*bb4ee6a4SAndroid Build Coastguard Worker
103*bb4ee6a4SAndroid Build Coastguard Worker let mut panic_output = String::new();
104*bb4ee6a4SAndroid Build Coastguard Worker // Ignore errors and print what we got.
105*bb4ee6a4SAndroid Build Coastguard Worker let _ = read_file.read_to_string(&mut panic_output);
106*bb4ee6a4SAndroid Build Coastguard Worker // Split by line because the logging facilities do not handle embedded new lines well.
107*bb4ee6a4SAndroid Build Coastguard Worker for line in panic_output.lines() {
108*bb4ee6a4SAndroid Build Coastguard Worker error!("{}", line);
109*bb4ee6a4SAndroid Build Coastguard Worker }
110*bb4ee6a4SAndroid Build Coastguard Worker }
111*bb4ee6a4SAndroid Build Coastguard Worker
112*bb4ee6a4SAndroid Build Coastguard Worker /// The intent of our panic hook is to get panic info and a stacktrace into the syslog, even for
113*bb4ee6a4SAndroid Build Coastguard Worker /// jailed subprocesses. It will always abort on panic to ensure a minidump is generated.
114*bb4ee6a4SAndroid Build Coastguard Worker ///
115*bb4ee6a4SAndroid Build Coastguard Worker /// Note that jailed processes will usually have a stacktrace of <unknown> because the backtrace
116*bb4ee6a4SAndroid Build Coastguard Worker /// routines attempt to open this binary and are unable to do so in a jail.
set_panic_hook()117*bb4ee6a4SAndroid Build Coastguard Worker pub fn set_panic_hook() {
118*bb4ee6a4SAndroid Build Coastguard Worker let default_panic = panic::take_hook();
119*bb4ee6a4SAndroid Build Coastguard Worker panic::set_hook(Box::new(move |info| {
120*bb4ee6a4SAndroid Build Coastguard Worker log_panic_info(default_panic.as_ref(), info);
121*bb4ee6a4SAndroid Build Coastguard Worker // Abort to trigger the crash reporter so that a minidump is generated.
122*bb4ee6a4SAndroid Build Coastguard Worker abort();
123*bb4ee6a4SAndroid Build Coastguard Worker }));
124*bb4ee6a4SAndroid Build Coastguard Worker }
125