Open danslo opened 1 year ago
cc @neild @tombergan
FWIW the URL in question is https://enablewebdesign.com
- let me know if you need any other information or a more local way to reproduce..
curl exits immediately:
$ curl --http2 https://enablewebdesign.com
curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)
Verified with go 1.19.5 @ darwin/arm64. For the record, this issue effectively creates a DoS, since there is (afaik?) no other way to enforce a timeout.
here is the crux of the issue, a panic
golang.org/x/net/http2.(*ClientConn).readLoop.func1()
/home/go/src/golang.org/x/net/http2/transport.go:2118 +0x26 fp=0xc000157b48 sp=0xc000157b30 pc=0x7016c6
runtime.deferCallSave(0xc000157c18, 0xc000157fa0?)
/usr/local/go/src/runtime/panic.go:796 +0x88 fp=0xc000157b58 sp=0xc000157b48 pc=0x4386e8
runtime.runOpenDeferFrame(0xc0004493b0?, 0xc000449360)
/usr/local/go/src/runtime/panic.go:769 +0x1a5 fp=0xc000157ba0 sp=0xc000157b58 pc=0x438505
panic({0x7dd3a0, 0xb44700})
/usr/local/go/src/runtime/panic.go:884 +0x212 fp=0xc000157c60 sp=0xc000157ba0 pc=0x438952
runtime.panicmem(...)
/usr/local/go/src/runtime/panic.go:260
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:835 +0x2f6 fp=0xc000157cb0 sp=0xc000157c60 pc=0x44f576
golang.org/x/net/http2.(*pipe).Write(0xc000157d60?, {0xc0001aa1a0?, 0x180?, 0x75?})
/home/go/src/golang.org/x/net/http2/pipe.go:98 +0x17b fp=0xc000157d38 sp=0xc000157cb0 pc=0x6e6d7b
golang.org/x/net/http2.(*clientConnReadLoop).processData(0xc000157f98, 0xc00007eae0)
/home/go/src/golang.org/x/net/http2/transport.go:2669 +0x225 fp=0xc000157e00 sp=0xc000157d38 pc=0x704ac5
golang.org/x/net/http2.(*clientConnReadLoop).run(0xc000157f98)
/home/go/src/golang.org/x/net/http2/transport.go:2258 +0x6e5 fp=0xc000157f60 sp=0xc000157e00 pc=0x702625
golang.org/x/net/http2.(*ClientConn).readLoop(0xc0001b0180)
/home/go/src/golang.org/x/net/http2/transport.go:2119 +0x6f fp=0xc000157fc8 sp=0xc000157f60 pc=0x70158f
golang.org/x/net/http2.(*Transport).newClientConn.func1()
/home/go/src/golang.org/x/net/http2/transport.go:817 +0x26 fp=0xc000157fe0 sp=0xc000157fc8 pc=0x6fa566
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc000157fe8 sp=0xc000157fe0 pc=0x46bde1
created by golang.org/x/net/http2.(*Transport).newClientConn
/home/go/src/golang.org/x/net/http2/transport.go:817 +0xc5c
The actual issue is failing to treat a 101 response as a PROTOCOL_ERROR.
Change https://go.dev/cl/463095 mentions this issue: http2: reject 101 status code
Change https://go.dev/cl/463497 mentions this issue: http2: reject DATA frames read before headers are finished
I don't think the handling of 101 responses is the problem; we'd encounter the same issue if the server had sent a 102 instead. The issue is that we're trying to process a DATA frame (or what looks like one) before the final headers have been read., which causes an internal panic. The panic is then obscured by a deferred cleanup function that gets stuck waiting on a mutex that was locked when the panic happened.
Doesn't your fix handle https://github.com/golang/go/issues/43965 as well?
Yes, #43965 looks to be the same thing. And you have a fix for that one in CL https://go.dev/cl/330415 (exact same change as my CL, heh) which seems to have fallen through the Gerrit cracks somehow.
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (
go env
)?Darwin / ARM64 / AMD64, but also reproduced on Linux / AMD64.
What did you do?
Run the following code with server that completes TLS handshake, but then just sends random raw data. In other words, our client never receives frame with
flags=END_STREAM
.What did you expect to see?
After 3 seconds, the connection is aborted.
What did you see instead?
The connection hangs indefinitely.
Additional Context
We've seen this happen with Cloudflare in front of a non-http(s) backend. In this case we assume it is wrongly configured to point at a SSH daemon. Even in that case, we should not hang forever.
Running with
GODEBUG=http2debug=2
:x/net/http2 also provides a
ReadIdleTimeout
andPingTimeout
. According to the docs:So the following was tried:
This still hangs indefinitely.