blob: f0b6434490f88050420f5f57e3d599917a8bac96 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! A panic handler for better crash signatures for rust apps.
use nix::sys::memfd::memfd_create;
use nix::sys::memfd::MemFdCreateFlag;
use std::ffi::CString;
use std::fs::File;
use std::io::Result;
use std::io::Write;
use std::mem;
use std::os::unix::prelude::FromRawFd;
use std::panic;
use std::process::abort;
const PANIC_MEMFD_NAME: &str = "RUST_PANIC_SIG";
fn create_panic_memfd() -> nix::Result<File> {
let fd: i32 = memfd_create(
&CString::new(PANIC_MEMFD_NAME).unwrap(),
MemFdCreateFlag::MFD_CLOEXEC | MemFdCreateFlag::MFD_ALLOW_SEALING,
)?;
// SAFETY: Safe since the fd is newly created and not owned yet. `File` will own the fd.
Ok(unsafe { File::from_raw_fd(fd) })
}
// TODO(b/309651697): This was written to be compatible with existing PanicInfo formatting, but it
// should probably be made more stable.
fn format_panic_info<W: Write>(w: &mut W, panic_info: &panic::PanicInfo<'_>) -> Result<()> {
write!(w, "panicked at '")?;
if let Some(message) = panic_info.message() {
write!(w, "{}", message)?;
} else if let Some(message) = panic_info.payload().downcast_ref::<&'static str>() {
write!(w, "{}", message)?;
}
write!(w, "', ")?;
// At the time of writing, `PanicInfo::location()` cannot return `None`.
match panic_info.location() {
Some(location) => {
write!(w, "{}", location)?;
}
None => {
write!(w, "no location info")?;
}
}
Ok(())
}
/// Inserts a panic handler that writes the panic info to a memfd called
/// "RUST_PANIC_SIG" before calling the original panic handler. This
/// makes it possible for external crash handlers to recover the panic info.
pub fn install_memfd_handler() {
let hook = panic::take_hook();
panic::set_hook(Box::new(move |p| {
// On failure, ignore the error and call the original handler.
if let Ok(mut panic_memfd) = create_panic_memfd() {
let _ = format_panic_info(&mut panic_memfd, p);
// Intentionally leak panic_memfd so it is picked up by the crash handler.
mem::forget(panic_memfd);
}
hook(p);
// If this is a multithreaded program, a panic in one thread will not kill the whole
// process. Abort so the entire process gets killed and produces a core dump.
abort();
}));
}