http2: advertise and enforce hpack max header list size
Thanks to Andy Bursavich for the example attack.
Pair programmed with Dmitri Shuralyov.
Fixes golang/go#12843
Change-Id: Ic412c9364b37a10e5164232aa809b956874fae08
Reviewed-on: https://go-review.googlesource.com/15751
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/http2/server.go b/http2/server.go
index 0ee96ec..2f95bdb 100644
--- a/http2/server.go
+++ b/http2/server.go
@@ -220,6 +220,7 @@
sc.inflow.add(initialWindowSize)
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
sc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, sc.onNewHeaderField)
+ sc.hpackDecoder.SetMaxHeaderListSize(sc.maxHeaderListSize())
fr := NewFramer(sc.bw, c)
fr.SetMaxReadFrameSize(srv.maxReadFrameSize())
@@ -353,7 +354,7 @@
streams map[uint32]*stream
initialWindowSize int32
headerTableSize uint32
- maxHeaderListSize uint32 // zero means unknown (default)
+ peerMaxHeaderListSize uint32 // zero means unknown (default)
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
req requestParam // non-zero while reading request headers
writingFrame bool // started write goroutine but haven't heard back on wroteFrameCh
@@ -370,6 +371,18 @@
hpackEncoder *hpack.Encoder
}
+func (sc *serverConn) maxHeaderListSize() uint32 {
+ n := sc.hs.MaxHeaderBytes
+ if n == 0 {
+ n = http.DefaultMaxHeaderBytes
+ }
+ // http2's count is in a slightly different unit and includes 32 bytes per pair.
+ // So, take the net/http.Server value and pad it up a bit, assuming 10 headers.
+ const perFieldOverhead = 32 // per http2 spec
+ const typicalHeaders = 10 // conservative
+ return uint32(n + typicalHeaders*perFieldOverhead)
+}
+
// requestParam is the state of the next request, initialized over
// potentially several frames HEADERS + zero or more CONTINUATION
// frames.
@@ -602,6 +615,7 @@
write: writeSettings{
{SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
{SettingMaxConcurrentStreams, sc.advMaxStreams},
+ {SettingMaxHeaderListSize, sc.maxHeaderListSize()},
// TODO: more actual settings, notably
// SettingInitialWindowSize, but then we also
@@ -1100,7 +1114,7 @@
case SettingMaxFrameSize:
sc.writeSched.maxFrameSize = s.Val
case SettingMaxHeaderListSize:
- sc.maxHeaderListSize = s.Val
+ sc.peerMaxHeaderListSize = s.Val
default:
// Unknown setting: "An endpoint that receives a SETTINGS
// frame with any unknown or unsupported identifier MUST