Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/crypto/ssh: Channel.Read incorrectly returns write error instead of io.EOF #45912

Open
hwh33 opened this issue May 2, 2021 · 3 comments
Open
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@hwh33
Copy link

hwh33 commented May 2, 2021

What version of Go are you using (go version)?

go version go1.16.3 darwin/amd64

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/hwh33/Library/Caches/go-build"
GOENV="/Users/hwh33/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/hwh33/Code/golang/pkg/mod"
GONOPROXY="github.com/getlantern/*"
GONOSUMDB="github.com/getlantern/*"
GOOS="darwin"
GOPATH="/Users/hwh33/Code/golang"
GOPRIVATE="github.com/getlantern/*"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.16.3"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/hwh33/Code/golang/src/github.com/getlantern/ossh/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/cm/89l8fqlj6c5dc09fc9mb14dh0000gn/T/go-build3841693344=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

  • Set up a pair of ssh.Conns (client and server).
  • Open a channel between the connections.
  • Write a bunch of data to the client side of the channel.
  • Close the client connection.
  • Read from the server channel.

Example:
https://play.golang.org/p/JAd3orfmiye

(The Go Playground sometimes sees the TCP dial/listen as a deadlock. It will often work though.)

What did you expect to see?

The data from the peer, then io.EOF.

What did you see instead?

Partial data, and a write-related error. Usually write: broken pipe or write: protocol wrong type for socket

More details

This only happens if the peer has closed the ssh.Conn (or the underlying transport). I believe the bug is rooted in an incorrect assumption here. The comment indicates that the channel should be tolerant of errors adjusting the window when the peer has closed the connection. The assumption is that such errors will be exclusively io.EOF. Perhaps something has changed, but this is not currently the case.

I'm actually not sure what the fix should be here as I'm not sure what error could be expected. I don't think net.Conn.Write makes any kind of guarantees about behavior when the peer has closed the connection. Somehow though, the buffered data should be returned to the reader, followed by io.EOF.

@gopherbot gopherbot added this to the Unreleased milestone May 2, 2021
@hwh33
Copy link
Author

hwh33 commented May 2, 2021

It seems like there's another issue beyond the block of code I linked to above (this one). Even if you ignore the error returned by c.adjustWindow there, the Channel.Read call still sometimes returns an early io.EOF.

@cagedmantis cagedmantis added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label May 5, 2021
@cagedmantis
Copy link
Contributor

/cc @FiloSottile

@willmo
Copy link

willmo commented Dec 30, 2022

https://go-review.googlesource.com/c/crypto/+/459915 would make encountering this less likely (as a side effect), since c.adjustWindow would write less often.

I thought about making another CL to simply ignore all errors from c.adjustWindow or its call to c.sendMessage, but I'm not sure if that's really a sensible thing to do. But returning all non-EOF errors seems wrong too.

Even if you ignore the error returned by c.adjustWindow there, the Channel.Read call still sometimes returns an early io.EOF.

I don't think it's strictly guaranteed that the data in your Channel.Write made it out to the underlying net.Conn before you closed it, even though you waited for Channel.Write to return. For example, writes are buffered during periodic rekeying. Other than that, though, I don't know why this would happen with your playground example, since the server should read and buffer the channel data before seeing the connection close, and buffer.Read doesn't return EOF until all buffered data is consumed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants