| // 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(); | |
| })); | |
| } |