tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 13698e897857d5dcd752b607f34cb43dfa95c817
parent e6b94ac6d400a933d1d7b7edc9dc8f38e0df4045
Author: Alex Franchuk <afranchuk@mozilla.com>
Date:   Thu,  8 Jan 2026 19:01:58 +0000

Bug 1986095 - Close unused fds when the crashreporter client starts r=gsvelto

Differential Revision: https://phabricator.services.mozilla.com/D278155

Diffstat:
Mtoolkit/crashreporter/client/app/src/main.rs | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+), 0 deletions(-)

diff --git a/toolkit/crashreporter/client/app/src/main.rs b/toolkit/crashreporter/client/app/src/main.rs @@ -89,12 +89,21 @@ fn main() { #[cfg(not(mock))] fn report_main() { + // Close unused fds before doing anything else, which might open some. + #[cfg(unix)] + let fd_cleanup_error = fd_cleanup::cleanup_unused_fds(); + let log_target = logging::init(); let mut config = Config::new(); config.log_target = Some(log_target); config.read_from_environment(); + #[cfg(unix)] + if let Err(e) = fd_cleanup_error { + log::warn!("fd cleanup failed: {e}"); + } + let mut config = Arc::new(config); match try_run(&mut config) { @@ -261,6 +270,72 @@ fn try_run(config: &mut Arc<Config>) -> anyhow::Result<bool> { } } +/// Close inherited fds which we don't need. +/// +/// This is important since we may re-launch Firefox (which may crash again, accumulating open fds +/// all the while). See bug 1986095. +/// +/// The `close_fds` crate does this in a more comprehensive way, so we may consider vendoring that in +/// the future. +#[cfg(all(unix, not(mock)))] +mod fd_cleanup { + unsafe extern "C" { + fn close(fd: std::ffi::c_int) -> std::ffi::c_int; + } + + pub fn cleanup_unused_fds() -> anyhow::Result<()> { + use anyhow::Context; + + let fd_dir: &str = match std::env::consts::OS { + "linux" => "/proc/self/fd", + "macos" => "/dev/fd", + os => anyhow::bail!("unimplemented for target os {os}"), + }; + + let dir = + std::fs::read_dir(fd_dir).with_context(|| format!("failed to enumerate {fd_dir}"))?; + + // Aggregate the fds to close so that we don't close the ReadDir fd (we could get this fd by + // using libc/nix, but at the time of this writing those crates aren't dependencies, and the + // workaround is simple enough). + let mut fds_to_close = Vec::new(); + for entry_result in dir { + let entry = match entry_result { + Ok(entry) => entry, + Err(e) => { + log::warn!("failed to enumerate {fd_dir}: {e}"); + continue; + } + }; + let filename = entry.file_name(); + // These should all be valid utf-8 + let Some(filename) = filename.to_str() else { + continue; + }; + let fd: std::os::fd::RawFd = match filename.parse() { + Ok(n) => n, + Err(e) => { + log::warn!("failed to parse {filename} as an fd: {e}"); + continue; + } + }; + // Ignore negative, stdin (0), stdout (1), or stderr(2) fds. + if fd >= 3 { + fds_to_close.push(fd); + } + } + + for fd in fds_to_close { + // We can ignore errors (e.g. inevitably there will be at least one error from closing the + // ReadDir fd which is now closed). We use libc `close` directly since OwnedFd has the + // invariant that the fd is open (and has a debug assert checking this). + unsafe { close(fd) }; + } + + Ok(()) + } +} + // `std` uses `raw-dylib` to link this dll, but that doesn't work properly on x86 MinGW, so we explicitly // have to link it. #[cfg(all(target_os = "windows", target_env = "gnu"))]