8000 Isolated Processes 11: Use a user-chosen handler for drop failures by marti4d · Pull Request #199 · rust-minidump/minidump-writer · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion crates/linux/process-backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ testing = []

[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
libc.workspace = true
log.workspace = true
process-reader = { version = "0.1.0", path = "../../process-reader", features = ["serde"] }
serde.workspace = true
thiserror.workspace = true
54 changes: 54 additions & 0 deletions crates/linux/process-backend/src/drop_fail_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Set a custom handler for when a Drop failure occurs
//!
//! Normally, there is not much that can be done when an error occurs during execution of `drop()`
//! -- Common strategies are to just ignore it, or to just log it and ignore it.
//!
//! We prefer the latter approach, but the issue is that if a Rust global logger has been set up
//! in the crashed process that will run the Executor, that could result in `log` being used in
//! a signal handler context. And since common logging behavior is to print to stderr, that will
//! almost-certainly cause a deadlock or some kind of other re-entrancy issue.
//!
//! Instead, we don't prescribe a behavior - We allow the user of minidump-writer to set their
//! own handler for failed `drop()` calls. If the user knows it's safe to log or print or
//! whatever, they can do that.
//!
//! Note that using [`install_global()`] to install a new handler will always overwrite any
//! previously-installed handler. This is intentional, as the user of this crate may want to
//! disable a previously-installed handler after a crash occurs (if that handler would cause
//! re-entrancy issues)

use core::sync::atomic::{AtomicPtr, Ordering};

static HANDLER: AtomicPtr<Handler> = AtomicPtr::new(core::ptr::null_mut());

pub struct Handler {
pub(crate) f: for<'a> fn(core::fmt::Arguments<'a>),
}

impl Handler {
/// Decorate the given function as a potential global Drop handler
pub const fn new(f: for<'a> fn(core::fmt::Arguments<'a>)) -> Self {
Self { f }
}
/// Install this handler as the new global Drop handler
///
/// Will overwrite any previously installed handler
pub fn install_global(&'static self) {
Comment thread
marti4d marked this conversation as resolved.
Resolved
HANDLER.store((self as *const Handler).cast_mut(), Ordering::Release);
}
}

pub(crate) fn get() -> Option<&'static Handler> {
let ptr: *const Handler = HANDLER.load(Ordering::Acquire).cast_const();
if ptr.is_null() {
None
} else {
Some(unsafe { &*ptr })
}
}

macro_rules! report_drop_failed(($($arg: tt)*) => {{
if let Some(handler) = $crate::drop_fail_handler::get() {
(handler.f)(::core::format_args!($($arg)*));
}
}});
5 changes: 5 additions & 0 deletions crates/linux/process-backend/src/lib.rs
< 8000 td id="diff-71508cb62326e13f76a7d6b55bcef8d3d1e60a1a0dd5d234ba47870812f45504HL1" class="blob-num blob-num-hunk non-expandable" data-line-number="...">
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#![no_std]
#![cfg(any(target_os = "linux", target_os = "android"))]

pub use drop_fail_handler::Handler as DropFailHandler;

#[macro_use]
mod drop_fail_handler;

pub mod local;
pub mod regs;

Expand Down
4 changes: 2 additions & 2 deletions crates/linux/process-backend/src/local/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ impl Drop for DirReader {
fn drop(&mut self) {
let rv = unsafe { libc::closedir(self.dirp) };
if rv == -1 {
log::debug!("failed to close directory: {}", errno());
report_drop_failed!("failed to close directory: {}", errno());
}
}
}
Expand Down Expand Up @@ -418,7 +418,7 @@ impl Drop for OwnedFd {
fn drop(&mut self) {
let rv = unsafe { libc::close(self.0) };
if rv == -1 {
log::error!("failed to close file: {}", errno());
report_drop_failed!("failed to close file: {}", errno());
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/linux/process-backend/src/local/module_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl Drop for Mapped {
fn drop(&mut self) {
let rv = unsafe { libc::munmap(self.ptr, self.len) };
if rv == -1 {
log::error!("failed to unmap memory: {}", errno());
report_drop_failed!("failed to unmap memory: {}", errno());
}
}
}
11 changes: 11 additions & 0 deletions src/linux/process_inspection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub enum Backend {

impl ProcessInspector {
pub fn local(pid: libc::pid_t) -> Self {
set_process_backend_drop_fail_handler();

let backend = local::Backend::new(pid);

ProcessInspector {
Expand Down Expand Up @@ -258,3 +260,12 @@ pub enum Error {
#[error("an error occurred running a syscall directly")]
Local(#[source] local::Error),
}

fn set_process_backend_drop_fail_handler() {
fn drop_fail_handler(args: core::fmt::Arguments<'_>) {
log::error!("a drop() error occurred in process-backend: {args}");
}
const HANDLER: process_backend::DropFailHandler =
process_backend::DropFailHandler::new(drop_fail_handler);
HANDLER.install_global();
}
Loading
0