Skip to content

fix: ssh socket hygiene#522

Open
CertainLach wants to merge 1 commit into
DeterminateSystems:mainfrom
deltarocks:push-zykqmstvtxtp
Open

fix: ssh socket hygiene#522
CertainLach wants to merge 1 commit into
DeterminateSystems:mainfrom
deltarocks:push-zykqmstvtxtp

Conversation

@CertainLach

@CertainLach CertainLach commented Jun 24, 2026

Copy link
Copy Markdown

Motivation

I have experienced my nix builds getting stuck on reading from remote build hook, in Worker::waitForInput(), where it polls on the socket that belongs to the ssh master process, and waits for it to be EOF, but it never is, as the command is persistent ssh master.

I'm not sure if this is a symptom of a larger problem I have somewhere in my setup, but it seems that ssh master should not keep references to stdin/stderr of the remote build hook process (ssh master is only supposed to "echo started" here).

Unfortunately I'm not sure how to reproduce that.

Context

Summary by CodeRabbit

  • Bug Fixes
    • Improved SSH control master startup on non-Windows systems by cleaning up process input/output handling.
    • Reduced the chance of stray file descriptors being left open during launch, helping improve stability and reliability.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@CertainLach, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 50 minutes and 12 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ecdbdf01-e8e4-46e3-9d70-7dbd4d366d07

📥 Commits

Reviewing files that changed from the base of the PR and between 351dccb and c537804.

📒 Files selected for processing (1)
  • src/libstore/ssh.cc
📝 Walkthrough

Walkthrough

The SSH master child setup now opens /dev/null, redirects stdin and stderr to it, keeps stdout on the pipe write end, and closes extra file descriptors after the redirects.

Changes

SSH master child file descriptor setup

Layer / File(s) Summary
Redirect stdio and close extras
src/libstore/ssh.cc
The SSH master child process opens /dev/null, throws SysError("opening /dev/null") on failure, redirects stdin and stderr to it, keeps stdout on the pipe write side, and calls unix::closeExtraFDs() after the dup2 calls.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A bunny hopped through the SSH night,
with /dev/null tucked out of sight.
Stdout sang through the pipe so neat,
stdin and stderr took a seat.
Extra fds got whisked away—
hop, hop, hooray! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and clearly points to the SSH-related cleanup reflected in the change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
src/libstore/ssh.cc (1)

270-271: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redirecting the SSH master's stderr to /dev/null discards all diagnostics, including -v output.

Below at Line 276 -v is appended when verbosity >= lvlChatty, but its output now goes to /dev/null, so chatty/debug runs lose ssh master diagnostics that previously reached the inherited stderr. If the goal is only to drop references to the build hook's stdin/stderr, consider routing the master's stderr to logFD (as startCommand does at Line 183) when available, falling back to /dev/null otherwise.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/libstore/ssh.cc` around lines 270 - 271, The SSH master setup currently
redirects STDERR_FILENO to /dev/null in ssh.cc, which hides useful diagnostics
including the -v output added in the SSH command construction. Update the
startup path around the stderr dup2 logic in the SSH master launch flow to
prefer routing stderr to logFD like startCommand does, and only fall back to
/dev/null when no logFD is available so chatty/debug verbosity still reaches
logs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/libstore/ssh.cc`:
- Around line 270-271: The SSH master setup currently redirects STDERR_FILENO to
/dev/null in ssh.cc, which hides useful diagnostics including the -v output
added in the SSH command construction. Update the startup path around the stderr
dup2 logic in the SSH master launch flow to prefer routing stderr to logFD like
startCommand does, and only fall back to /dev/null when no logFD is available so
chatty/debug verbosity still reaches logs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d991621-8bd8-4837-8552-f1621e9c42ad

📥 Commits

Reviewing files that changed from the base of the PR and between 923cd70 and 351dccb.

📒 Files selected for processing (1)
  • src/libstore/ssh.cc

@CertainLach

Copy link
Copy Markdown
Author

Added use of logFD when exists

Comment thread src/libstore/ssh.cc
throw SysError("duping over stdout");
if (dup2(nullFd, STDIN_FILENO) == -1)
throw SysError("duping over stdin");
if (dup2(logFD != -1 ? logFD : nullFd, STDERR_FILENO) == -1)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not sure if we should redirect stderr to /dev/null, since that could hide useful error messages if the master ssh fails to connect.

@CertainLach CertainLach Jun 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The problem is in my case I was seeing nix-daemon being stuck on Worker::waitForInput, where fd it waits for belongs to stderr of ssh control master

I'll try to add debug logs to understand what is happening here, but it seems that stderr should not leak to the child process here in any case

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

...At least in context of remote build hook

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.

2 participants