aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilippo Valsorda <filippo@golang.org>2019-08-12 16:45:15 -0400
committerFilippo Valsorda <valsorda@google.com>2019-08-12 21:44:22 +0000
commit7139b45d1410ded14e1e131151fd8dfc435ede6c (patch)
tree363749f35fdd50a2b7ec3d0a04f040685b89dd0a
parent7f416b4f048677d0784e6941516c0f1e6052b2d6 (diff)
downloadgo-7139b45d1410ded14e1e131151fd8dfc435ede6c.tar.gz
go-7139b45d1410ded14e1e131151fd8dfc435ede6c.zip
[release-branch.go1.12-security] net/http: update bundled http2 to import security fix
Apply the following unpublished golang.org/x/net commit. commit cdfb69ac37fc6fa907650654115ebebb3aae2087 Author: Filippo Valsorda <filippo@golang.org> Date: Sun Aug 11 02:12:18 2019 -0400 [release-branch.go1.12] http2: limit number of control frames in server send queue An attacker could cause servers to queue an unlimited number of PING ACKs or RST_STREAM frames by soliciting them and not reading them, until the program runs out of memory. Limit control frames in the queue to a few thousands (matching the limit imposed by other vendors) by counting as they enter and exit the scheduler, so the protection will work with any WriteScheduler. Once the limit is exceeded, close the connection, as we have no way to communicate with the peer. Change-Id: I842968fc6ed3eac654b497ade8cea86f7267886b Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/525552 Reviewed-by: Brad Fitzpatrick <bradfitz@google.com> (cherry picked from commit 589ad6cc5321fb68a90370348a241a5da0a2cc80) Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/526069 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Fixes CVE-2019-9512 and CVE-2019-9514 Updates #33606 Change-Id: I282b3e0fa22422d9ea0d07f4a3935685ce4a7433 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/526071 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
-rw-r--r--src/net/http/h2_bundle.go54
1 files changed, 45 insertions, 9 deletions
diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go
index f714cbb9a1..5db88afdde 100644
--- a/src/net/http/h2_bundle.go
+++ b/src/net/http/h2_bundle.go
@@ -3610,10 +3610,11 @@ func (p *http2pipe) Done() <-chan struct{} {
}
const (
- http2prefaceTimeout = 10 * time.Second
- http2firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway
- http2handlerChunkWriteSize = 4 << 10
- http2defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to?
+ http2prefaceTimeout = 10 * time.Second
+ http2firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway
+ http2handlerChunkWriteSize = 4 << 10
+ http2defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to?
+ http2maxQueuedControlFrames = 10000
)
var (
@@ -3721,6 +3722,15 @@ func (s *http2Server) maxConcurrentStreams() uint32 {
return http2defaultMaxStreams
}
+// maxQueuedControlFrames is the maximum number of control frames like
+// SETTINGS, PING and RST_STREAM that will be queued for writing before
+// the connection is closed to prevent memory exhaustion attacks.
+func (s *http2Server) maxQueuedControlFrames() int {
+ // TODO: if anybody asks, add a Server field, and remember to define the
+ // behavior of negative values.
+ return http2maxQueuedControlFrames
+}
+
type http2serverInternalState struct {
mu sync.Mutex
activeConns map[*http2serverConn]struct{}
@@ -4040,6 +4050,7 @@ type http2serverConn struct {
sawFirstSettings bool // got the initial SETTINGS frame after the preface
needToSendSettingsAck bool
unackedSettings int // how many SETTINGS have we sent without ACKs?
+ queuedControlFrames int // control frames in the writeSched queue
clientMaxStreams uint32 // SETTINGS_MAX_CONCURRENT_STREAMS from client (our PUSH_PROMISE limit)
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
curClientStreams uint32 // number of open streams initiated by the client
@@ -4431,6 +4442,14 @@ func (sc *http2serverConn) serve() {
}
}
+ // If the peer is causing us to generate a lot of control frames,
+ // but not reading them from us, assume they are trying to make us
+ // run out of memory.
+ if sc.queuedControlFrames > sc.srv.maxQueuedControlFrames() {
+ sc.vlogf("http2: too many control frames in send queue, closing connection")
+ return
+ }
+
// Start the shutdown timer after sending a GOAWAY. When sending GOAWAY
// with no error code (graceful shutdown), don't start the timer until
// all open streams have been completed.
@@ -4632,6 +4651,14 @@ func (sc *http2serverConn) writeFrame(wr http2FrameWriteRequest) {
}
if !ignoreWrite {
+ if wr.isControl() {
+ sc.queuedControlFrames++
+ // For extra safety, detect wraparounds, which should not happen,
+ // and pull the plug.
+ if sc.queuedControlFrames < 0 {
+ sc.conn.Close()
+ }
+ }
sc.writeSched.Push(wr)
}
sc.scheduleFrameWrite()
@@ -4749,10 +4776,8 @@ func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
// If a frame is already being written, nothing happens. This will be called again
// when the frame is done being written.
//
-// If a frame isn't being written we need to send one, the best frame
-// to send is selected, preferring first things that aren't
-// stream-specific (e.g. ACKing settings), and then finding the
-// highest priority stream.
+// If a frame isn't being written and we need to send one, the best frame
+// to send is selected by writeSched.
//
// If a frame isn't being written and there's nothing else to send, we
// flush the write buffer.
@@ -4780,6 +4805,9 @@ func (sc *http2serverConn) scheduleFrameWrite() {
}
if !sc.inGoAway || sc.goAwayCode == http2ErrCodeNo {
if wr, ok := sc.writeSched.Pop(); ok {
+ if wr.isControl() {
+ sc.queuedControlFrames--
+ }
sc.startFrameWrite(wr)
continue
}
@@ -5072,6 +5100,8 @@ func (sc *http2serverConn) processSettings(f *http2SettingsFrame) error {
if err := f.ForeachSetting(sc.processSetting); err != nil {
return err
}
+ // TODO: judging by RFC 7540, Section 6.5.3 each SETTINGS frame should be
+ // acknowledged individually, even if multiple are received before the ACK.
sc.needToSendSettingsAck = true
sc.scheduleFrameWrite()
return nil
@@ -9402,7 +9432,7 @@ type http2WriteScheduler interface {
// Pop dequeues the next frame to write. Returns false if no frames can
// be written. Frames with a given wr.StreamID() are Pop'd in the same
- // order they are Push'd.
+ // order they are Push'd. No frames should be discarded except by CloseStream.
Pop() (wr http2FrameWriteRequest, ok bool)
}
@@ -9446,6 +9476,12 @@ func (wr http2FrameWriteRequest) StreamID() uint32 {
return wr.stream.id
}
+// isControl reports whether wr is a control frame for MaxQueuedControlFrames
+// purposes. That includes non-stream frames and RST_STREAM frames.
+func (wr http2FrameWriteRequest) isControl() bool {
+ return wr.stream == nil
+}
+
// DataSize returns the number of flow control bytes that must be consumed
// to write this entire frame. This is 0 for non-DATA frames.
func (wr http2FrameWriteRequest) DataSize() int {