Skip to content

Terminate Linux proxy watcher when parent exits - #20

Closed
PHPLego (phplego) wants to merge 1 commit into
microsoft:mainfrom
phplego:fix-linux-proxy-watcher-lifetime
Closed

Terminate Linux proxy watcher when parent exits#20
PHPLego (phplego) wants to merge 1 commit into
microsoft:mainfrom
phplego:fix-linux-proxy-watcher-lifetime

Conversation

@phplego

Copy link
Copy Markdown
Contributor

Summary

Terminate the Linux dconf watch / gsettings monitor child automatically when the process that created it exits, including shutdown paths where Rust destructors are not run.

Problem

While repeatedly launching and closing a VS Code Extension Development Host on Linux, I observed one additional process remaining after every closed window:

dconf watch /system/proxy/

The orphaned processes were reparented to the user systemd process and did not disappear over time. Each process owns an inotify instance. After enough development-host restarts, they exhausted the user's fs.inotify.max_user_instances limit (128 in the reproduced environment). New filesystem watchers then failed with EMFILE / Too many open files, causing VS Code extension filesystem change notifications to stop.

Closing the VS Code window normally, rather than killing it, produced the same leak.

The existing Watcher::drop implementation correctly kills and waits for the child. However, process shutdown does not guarantee that Rust destructors run. A process-owned watcher therefore needs an OS-level lifetime relationship as a fallback.

The repository currently has GitHub Issues disabled (has_issues: false), despite SUPPORT.md linking to Issues, so I am submitting the small fix directly as a pull request and documenting the complete report here.

Fix

Before executing either Linux watcher command, set PR_SET_PDEATHSIG to SIGTERM. The kernel will then terminate the watcher when its creating process dies.

The code also compares the post-prctl parent PID with the PID captured before fork, covering the race where the parent exits before the child installs its parent-death signal.

The existing Drop cleanup remains unchanged and continues to handle normal resolver disposal.

This is deliberately limited to the Linux child-process setup. It does not change the public API, resolver ownership, Node bindings, or behavior on other platforms.

Verification

  • Reproduced the leak with repeated VS Code Extension Development Host launch/close cycles.
  • Verified in an isolated parent/child test that the configured watcher disappears when its parent calls process::exit without running destructors.
  • Ran:
cargo test --lib --no-default-features --features pac-engine

Result: 59 passed, 0 failed.

Copilot AI balanced review requested due to automatic review settings August 13, 2026 06:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds OS-level cleanup for Linux proxy watcher processes when their parent exits unexpectedly.

Changes:

  • Configures watcher commands with PR_SET_PDEATHSIG.
  • Handles the parent-exit race after fork.
  • Adds Linux-specific libc dependency.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/platform/linux.rs Configures watcher child-process termination.
Cargo.toml Adds Linux libc dependency.
Cargo.lock Records the dependency update.
Suppressed comments (1)

src/platform/linux.rs:190

  • The new OS-level lifetime guarantee has no committed regression coverage, although this platform module already has unit and OS tests. Add a Linux subprocess test that verifies the watcher exits when its parent terminates without destructors, plus a case where a resolver created on a short-lived thread remains watched after that thread exits; the latter would also catch the parent-thread semantics of PR_SET_PDEATHSIG.
            // The parent may have exited between fork and PR_SET_PDEATHSIG.
            if libc::getppid() != expected_parent {
                libc::raise(libc::SIGTERM);

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Cargo.toml
Comment on lines +120 to +121
[target.'cfg(target_os = "linux")'.dependencies]
libc = "0.2"
Comment thread src/platform/linux.rs
Comment on lines +184 to +185
command.pre_exec(move || {
if libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) == -1 {
@phplego

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

@Rongronggg9

Rongrong (Rongronggg9) commented Aug 17, 2026

Copy link
Copy Markdown

I am replying here because the repository disables GitHub Issues. IOW, I am not reviewing the PR.

The issue not only leaks inotify instances, but also leaks ~4 GiB (!) shmem after closing VS Code. In an environment with a 16 GiB /dev/shm, launching and closing VS Code three times causes any subsequent launches to be killed with SIGBUS due to shmem exhaustion. Under memory pressure, shmem leakage could also lead to OOM.

COMMAND     PID     USER  FD   TYPE DEVICE SIZE/OFF NODE NAME
gsettings 43175 rongrong  66u   REG   0,32       4B   12 /dev/shm/.org.chromium.Chromium.A6qm5D (deleted)
gsettings 43175 rongrong  71r   REG   0,32   256.0K   13 /dev/shm/.org.chromium.Chromium.rwTd4o (deleted)
gsettings 43175 rongrong  73r   REG   0,32       4B   14 /dev/shm/.org.chromium.Chromium.APqEfP (deleted)
gsettings 43175 rongrong  98r   REG   0,32       8B   17 /dev/shm/.org.chromium.Chromium.FnbHaA (deleted)
gsettings 43175 rongrong 120u   REG   0,32     144B   23 /dev/shm/.org.chromium.Chromium.toQkcb (deleted)
gsettings 43175 rongrong 122u   REG   0,32     1.0M   24 /dev/shm/.org.chromium.Chromium.P6Wwzl (deleted)
gsettings 43175 rongrong 123u   REG   0,32     144B   26 /dev/shm/.org.chromium.Chromium.sC3RPy (deleted)
gsettings 43175 rongrong 124u   REG   0,32    64.0K   25 /dev/shm/.org.chromium.Chromium.A6dNEL (deleted)
gsettings 43175 rongrong 125u   REG   0,32     1.0M   27 /dev/shm/.org.chromium.Chromium.ukgY7s (deleted)
gsettings 43175 rongrong 127u   REG   0,32    64.0K   28 /dev/shm/.org.chromium.Chromium.yF4HKx (deleted)
gsettings 43175 rongrong 131r   REG   0,32   366.8K   49 /dev/shm/.org.chromium.Chromium.GmjWfs (deleted)
gsettings 43175 rongrong 137u   REG   0,32     2.0M   40 /dev/shm/.org.chromium.Chromium.7f5IqQ (deleted)
gsettings 43175 rongrong 139r   REG   0,32   784.9M   60 /dev/shm/.org.chromium.Chromium.vFb7zw (deleted)
gsettings 43175 rongrong 140r   REG   0,32   784.9M   61 /dev/shm/.org.chromium.Chromium.gJERd9 (deleted)
gsettings 43175 rongrong 143r   REG   0,32   784.9M   66 /dev/shm/.org.chromium.Chromium.3aQDOS (deleted)
gsettings 43175 rongrong 144r   REG   0,32   784.9M   62 /dev/shm/.org.chromium.Chromium.dCtYMg (deleted)
gsettings 43175 rongrong 145r   REG   0,32    18.6M   68 /dev/shm/.org.chromium.Chromium.OySLNL (deleted)
gsettings 43175 rongrong 146r   REG   0,32     1.2M   69 /dev/shm/.org.chromium.Chromium.QMqFK7 (deleted)
gsettings 43175 rongrong 153r   REG   0,32   784.9M   70 /dev/shm/.org.chromium.Chromium.7lakt3 (deleted)

The issue, ostensibly, consists of two dedicated bugs:

  • Child process leakage
  • FD leakage

In actual fact, the methodology of using dconf or gesttings utilities to monitor system proxy change events is dead wrong from the very beginning. Calling a long-running external utility from Electron's root process is the problem itself.

The codebase smells like vibe-coded. If this is the case, please tell the coding agent to check how Chromium implements the same functionality correctly using gio: https://github.com/chromium/chromium/blob/e5ad1c8587d09f5dbafcaf873c77982ebd13573b/net/proxy_resolution/proxy_config_service_linux.cc#L314. VS Code is already dynamically linked against libgio-2.0.so.0 due to the functionality provided by Chromium.

@chrmarti

Copy link
Copy Markdown
Contributor

Continuing in #21, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants