Skip to content

vendor: fix x/crypto ssh channel.SendRequest spinloop on closed channel (golang/go#79658)#1143

Merged
kart2bc merged 2 commits into
cloudfoundry:developfrom
navinms711:fix/xcrypto-drain-loop
Jun 2, 2026
Merged

vendor: fix x/crypto ssh channel.SendRequest spinloop on closed channel (golang/go#79658)#1143
kart2bc merged 2 commits into
cloudfoundry:developfrom
navinms711:fix/xcrypto-drain-loop

Conversation

@navinms711

@navinms711 navinms711 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

golang.org/x/crypto v0.52.0 — introduced into this repo by 6c4f72a (v0.50.0 → v0.52.0) — shipped a security fix for CVE-2026-39830 (3c7c869 — "ssh: fix deadlock on unexpected channel responses"). That fix added drain loops inside both channel.SendRequest and mux.SendRequest to flush any stale buffered replies before a new request is sent. Both loops have the same bug:

drain:
    for {
        select {
        case <-ch.msg:     // ← BUG: does not check comma-ok
        default:
            break drain
        }
    }

The loop does not use the comma-ok idiom (_, ok := <-ch.msg). When the underlying SSH channel has already been closed, ch.msg is a closed Go channel, and a receive from a closed channel always returns immediately with the zero value. The default arm is therefore never selected, and the loop spins at 100% CPU forever.

On Windows 2016 the server-side channel teardown completes fast enough that ch.msg is already closed before the caller's next SendRequest call. This caused the test at
src/code.cloudfoundry.org/diego-ssh/handlers/session_channel_handler_windows2016_test.go:718
("the session is no longer usable") to hang for ~59 minutes until the Ginkgo suite timeout fired.
The failure was deterministic across all three CI runs.

Fix

This PR bumps golang.org/x/crypto in go.mod to pseudo-version v0.52.1-0.20260528171630-4c4d20b72c2f, which pins the vendor tree to the exact upstream commits that fix both spinloops:

e3e62d9 — "ssh: fix spinloop in channel SendRequest drain on closed channel" (golang/go#79658)
4c4d20b — "ssh: fix spinloop in mux SendRequest drain on closed channel"

// Before
case <-ch.msg:
// After
case _, ok := <-ch.msg:
    if !ok {
        break drain
    }

The same fix applies symmetrically to mux.go's m.globalResponses drain loop.

Additional vendor changes

Because Go pseudo-versions are linear, v0.52.1-0.20260528171630-4c4d20b72c2f also includes the upstream auth-hardening commits that landed just before the spinloop fixes:
image
These are conservative hardening changes; none alter any SSH protocol behavior used by diego-ssh.

BOSH package specs

control.go introduces a new transitive import (golang.org/x/crypto/cryptobyte). The following BOSH package specs were updated to include the new dependency globs:

  • packages/buildpack_app_lifecycle/spec
  • packages/diego-sshd/spec
  • packages/docker_app_lifecycle/spec
  • packages/ssh_proxy/spec
  • packages/windows_app_lifecycle/spec

Durability

Using a go.mod pseudo-version rather than a raw vendor patch means the fix is durable:

The bump-dependencies periodic job runs go get -u, which will not downgrade from v0.52.1-... to v0.52.0 (pseudo-versions are treated as higher than the base tag).
When upstream tags v0.53.0 (which will include both spinloop fixes), the bump job will upgrade cleanly past the pseudo-version with no conflict.

Upstream references:

image

Backward Compatibility

Breaking Change? No

The change only affects behavior on an already-closed SSH channel — a case where the old code either returned io.EOF (pre-v0.52.0) or spun forever (v0.52.0 without this fix). Restoring a clean io.EOF return is strictly a bug fix and does not alter any public API or operator-visible behavior.

Notes

  • The existing test session_channel_handler_windows2016_test.go:718 acts as the regression test — no new tests required.
  • The inigo integration test suite also imports x/crypto/ssh but never calls SendRequest after channel close, so it is not affected.
  • cnb_app_lifecycle uses its own separate cnbapplifecycle/vendor/ tree and is unaffected by this change.

…145)

diego-logging-client (tnz-96145) removed grpc.WithBlock() from
NewIngressClient, making the loggregator dial non-blocking. ssh-proxy
now starts successfully even when the loggregator server is unavailable
and retries the connection in the background.

The test "when the loggregator server isn't up → exits with non-zero
status code" expected the old blocking behaviour. With the non-blocking
dial ssh-proxy never exits, causing the test to time out.

Update the assertion to verify the new correct behaviour: ssh-proxy
starts and keeps running when the loggregator server is temporarily
unavailable.

Co-authored-by: Cursor <cursoragent@cursor.com>
kart2bc
kart2bc previously approved these changes Jun 2, 2026
@github-project-automation github-project-automation Bot moved this from Inbox to Pending Merge | Prioritized in Application Runtime Platform Working Group Jun 2, 2026
Bump golang.org/x/crypto v0.52.0 → v0.52.1-0.20260528171630-4c4d20b72c2f
to pick up two upstream fixes for spinloops introduced by the CVE-2026-39830
security patch (3c7c869):

  e3e62d9 ssh: fix spinloop in channel SendRequest drain on closed channel
  4c4d20b ssh: fix spinloop in mux SendRequest drain on closed channel

Both fixes apply the comma-ok idiom to drain loops that spin forever when
ch.msg / m.globalResponses is already closed:

  // Before
  case <-ch.msg:

  // After
  case _, ok := <-ch.msg:
      if !ok {
          break drain
      }

The spinloop caused session_channel_handler_windows2016_test.go:718
("the session is no longer usable") to hang ~59 minutes on Windows 2016
until the Ginkgo suite timeout fired. The failure was deterministic
across all three CI runs.

Neither fix is in a tagged release yet; the pseudo-version pins go.mod,
go.sum, and vendor/ to the exact commits so a future go mod vendor run
will not silently drop the fix. When upstream tags v0.53.0 the periodic
bump-dependencies job will upgrade cleanly over this.

Upstream references:
  golang/go#79658
  golang/crypto@e3e62d9 (channel fix)
  golang/crypto@4c4d20b (mux fix)

Co-authored-by: Cursor <cursoragent@cursor.com>
@kart2bc kart2bc merged commit 6086f51 into cloudfoundry:develop Jun 2, 2026
16 of 17 checks passed
@github-project-automation github-project-automation Bot moved this from Pending Merge | Prioritized to Done in Application Runtime Platform Working Group Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants