Skip to content

[Go 1.23.0] CVE-2026-39830: ssh: fix deadlock on unexpected channel responses - #20

Open
Atharva-Shinde wants to merge 2 commits into
openshift-sustaining:sustaining-v0.41.0from
Atharva-Shinde:sustaining-v0.41.0
Open

[Go 1.23.0] CVE-2026-39830: ssh: fix deadlock on unexpected channel responses#20
Atharva-Shinde wants to merge 2 commits into
openshift-sustaining:sustaining-v0.41.0from
Atharva-Shinde:sustaining-v0.41.0

Conversation

@Atharva-Shinde

@Atharva-Shinde Atharva-Shinde commented Aug 19, 2026

Copy link
Copy Markdown

Process

git fetch upstream
git switch -c sustaining-v0.41.0 upstream/sustaining-v0.41.0
git am /tmp/fork-maintainer/GO-2026-5017/v0.41.0-GO-2026-5017-0.patch
git am /tmp/fork-maintainer/GO-2026-5017/v0.41.0-GO-2026-5017-1.patch

Conflicts

No conflicts — patches applied cleanly.

Patch Source

Verification

  • Runtime gates: none
Unit tests output
 crypto git:(sustaining-v0.41.0) go test ./...
?       golang.org/x/crypto/acme/autocert/internal/acmetest     [no test files]
?       golang.org/x/crypto/acme/internal/acmeprobe     [no test files]
?       golang.org/x/crypto/cryptobyte/asn1     [no test files]
?       golang.org/x/crypto/internal/testenv    [no test files]
?       golang.org/x/crypto/internal/wycheproof/internal/dsa    [no test files]
?       golang.org/x/crypto/openpgp/errors      [no test files]
?       golang.org/x/crypto/poly1305    [no test files]
ok      golang.org/x/crypto/acme        10.773s
ok      golang.org/x/crypto/acme/autocert       2.673s
ok      golang.org/x/crypto/argon2      2.993s
ok      golang.org/x/crypto/bcrypt      3.856s
ok      golang.org/x/crypto/blake2b     4.225s
ok      golang.org/x/crypto/blake2s     4.618s
ok      golang.org/x/crypto/blowfish    3.777s
ok      golang.org/x/crypto/bn256       5.131s
ok      golang.org/x/crypto/cast5       6.086s
ok      golang.org/x/crypto/chacha20    3.401s
ok      golang.org/x/crypto/chacha20poly1305    5.770s
ok      golang.org/x/crypto/cryptobyte  6.129s
ok      golang.org/x/crypto/curve25519  6.497s
ok      golang.org/x/crypto/ed25519     6.834s
ok      golang.org/x/crypto/hkdf        4.640s
ok      golang.org/x/crypto/internal/alias      4.768s
ok      golang.org/x/crypto/internal/poly1305   4.756s
ok      golang.org/x/crypto/internal/wycheproof 6.323s
ok      golang.org/x/crypto/md4 5.057s
ok      golang.org/x/crypto/nacl/auth   5.279s
ok      golang.org/x/crypto/nacl/box    5.104s
ok      golang.org/x/crypto/nacl/secretbox      5.015s
ok      golang.org/x/crypto/nacl/sign   4.812s
ok      golang.org/x/crypto/ocsp        4.999s
ok      golang.org/x/crypto/openpgp     6.512s
ok      golang.org/x/crypto/openpgp/armor       5.218s
?       golang.org/x/crypto/ssh/terminal        [no test files]
ok      golang.org/x/crypto/openpgp/clearsign   13.139s
ok      golang.org/x/crypto/openpgp/elgamal     5.232s
ok      golang.org/x/crypto/openpgp/packet      5.212s
ok      golang.org/x/crypto/openpgp/s2k 6.188s
ok      golang.org/x/crypto/otr 4.936s
ok      golang.org/x/crypto/pbkdf2      4.707s
ok      golang.org/x/crypto/pkcs12      4.834s
ok      golang.org/x/crypto/pkcs12/internal/rc2 4.969s
ok      golang.org/x/crypto/ripemd160   5.312s
ok      golang.org/x/crypto/salsa20     5.271s
ok      golang.org/x/crypto/salsa20/salsa       5.602s
ok      golang.org/x/crypto/scrypt      5.628s
ok      golang.org/x/crypto/sha3        7.155s
--- FAIL: TestParseRSAModulusTooLarge (0.00s)
    keys_test.go:219: unexpected error message: got "ssh: no key found", want substring "rsa modulus too large"
seen &ssh.rsaPublicKey{N:20061140430650092691700129633418895153104099440152571948017228333944623487573521043236463535114811698275540013288855432311158126420471092062212564127370505763070250534466667545557854500626223850938086149993496716374117138731870544047779767066551724960262812037558993204799983435256152044565444388235895913343541217854379451044881161906995318571403079108766806750251846358097485205923957951642423646975888286438168592280219572188076697918703120158605105665730880552260696034697922765719453812717299592545244658127905527709856843276462192052069837242904720629896824162245468755516134327672099510173872004590681225925311, E:65537}
seen &ssh.dsaPublicKey{Parameters:dsa.Parameters{P:175721116665844170017669647669946518528413457312022711032734739623005755152443227429734217171261249553795678598220451924215157415901674587334166902511516864954496725567843523544375611356526453671701341914365481186446303719656617243678901998494782637847628119023876650244387440525171537115728772449014481872071, Q:832165918110642932865296677897233797470791928577, G:45710335010743906175172359887947291464946349254698214926126198012254417027163534549930512227720529453527812583090856824470895904385213843846478788439650923858707625528883721182368810782074418622955266070744886120496437975321603647691691785855608495718858707179099826464685770134027417598116971534482882955896}, Y:156527414827976273333470358776490302124383937379242530509323609715449789732336069914169957260987424440278372270165376235659769589602997045461900077747446091360682548418273726245607350047870147307811900550045251583664693054861184446136606830815561121439743970433898383744356459189167096821175929531468417688750}
FAIL
FAIL    golang.org/x/crypto/ssh 6.558s
ok      golang.org/x/crypto/ssh/agent   5.099s
ok      golang.org/x/crypto/ssh/internal/bcrypt_pbkdf   5.332s
ok      golang.org/x/crypto/ssh/knownhosts      4.684s
ok      golang.org/x/crypto/ssh/test    6.972s
ok      golang.org/x/crypto/tea 4.973s
ok      golang.org/x/crypto/twofish     5.216s
ok      golang.org/x/crypto/x509roots/nss       5.159s
ok      golang.org/x/crypto/xtea        5.153s
ok      golang.org/x/crypto/xts 5.162s
FAIL

Previously, the mux implementation handled global request responses by
blocking until the response could be sent to the globalResponses channel.
Since this channel has a buffer size of 1, unsolicited responses from a
server (or responses arriving after a timeout) would fill the buffer.
Subsequent unsolicited responses would block handleGlobalPacket, stalling
the entire connection's read loop and causing a denial of service.

This change modifies handleGlobalPacket to use a non-blocking send. If
no goroutine is waiting for a response (or the buffer is full), the
message is dropped. This aligns with OpenSSH behavior, which ignores
unexpected global responses.

Additionally, SendRequest now drains the globalResponses channel after
acquiring the mutex but before sending the request. This ensures that
any stale responses or "spam" buffered just before the lock was acquired
are discarded, preventing race conditions where a legitimate request
might otherwise consume an unrelated response.

This issue was found during a security audit by NCC Group Cryptography
Services, sponsored by Teleport.

Fixes golang/go#79564
Fixes CVE-2026-39830

Change-Id: Ia0c46355203d557eadcd432c10b87c8a044e1089
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/781640
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Neal Patel <nealpatel@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Previously, channel.handlePacket sent channelRequestSuccess and
channelRequestFailure messages to ch.msg unconditionally via the default
arm of its type switch. Because ch.msg is a bounded buffer (chanSize),
a peer that sends a burst of unsolicited channel request responses for
an open, idle channel fills the buffer and blocks the mux read loop on
the next send. That stalls all packet processing on the connection,
and because readLoop then backs up on t.incoming, closing the
underlying net.Conn does not unblock either goroutine: user code
observes Close() returning promptly while Wait() hangs and the mux,
readLoop, and kexLoop goroutines leak permanently.

This change mirrors the fix for the mux-level SendRequest path: a
sentRequestPending atomic gate is set while a SendRequest with
WantReply is in flight, handlePacket drops responses when the gate is
closed, and uses a non-blocking send otherwise. SendRequest drains
any spurious response that slipped through before discarding it, so
the caller always observes the reply to its own request.

This aligns with OpenSSH, which silently ignores channel confirm
messages that do not match a pending request.

Fixes golang/go#79564
Fixes CVE-2026-39830

Change-Id: I15e2add4bf7876bb0c6f921f8b57203d97e83f47
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/781664
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Neal Patel <nealpatel@google.com>
Reviewed-by: Neal Patel <nealpatel@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
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