From a93df1424bc8e1e02bae3604046dc3476d9958a0 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Fri, 6 Oct 2023 14:16:27 -0700 Subject: [PATCH 1/2] [release-branch.go1.20] net/http: regenerate h2_bundle.go Pull in a security fix from x/net/http2: http2: limit maximum handler goroutines to MaxConcurrentStreamso For #63417 Fixes #63426 Fixes CVE-2023-39325 Change-Id: I6e32397323cd9b4114c990fcc9d19557a7f5f619 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2047401 Reviewed-by: Tatiana Bradley TryBot-Result: Security TryBots Run-TryBot: Damien Neil Reviewed-by: Ian Cottrell Reviewed-on: https://go-review.googlesource.com/c/go/+/534255 Reviewed-by: Dmitri Shuralyov Reviewed-by: Damien Neil TryBot-Bypass: Dmitri Shuralyov Reviewed-by: Michael Pratt Auto-Submit: Dmitri Shuralyov --- h2_bundle.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/h2_bundle.go b/h2_bundle.go index 1e0b83d4..1f6d264c 100644 --- a/h2_bundle.go +++ b/h2_bundle.go @@ -4320,9 +4320,11 @@ type http2serverConn struct { advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client curClientStreams uint32 // number of open streams initiated by the client curPushedStreams uint32 // number of open streams initiated by server push + curHandlers uint32 // number of running handler goroutines maxClientStreamID uint32 // max ever seen from client (odd), or 0 if there have been no client requests maxPushPromiseID uint32 // ID of the last push promise (even), or 0 if there have been no pushes streams map[uint32]*http2stream + unstartedHandlers []http2unstartedHandler initialStreamSendWindowSize int32 maxFrameSize int32 peerMaxHeaderListSize uint32 // zero means unknown (default) @@ -4718,6 +4720,8 @@ func (sc *http2serverConn) serve() { return case http2gracefulShutdownMsg: sc.startGracefulShutdownInternal() + case http2handlerDoneMsg: + sc.handlerDone() default: panic("unknown timer") } @@ -4765,6 +4769,7 @@ var ( http2idleTimerMsg = new(http2serverMessage) http2shutdownTimerMsg = new(http2serverMessage) http2gracefulShutdownMsg = new(http2serverMessage) + http2handlerDoneMsg = new(http2serverMessage) ) func (sc *http2serverConn) onSettingsTimer() { sc.sendServeMsg(http2settingsTimerMsg) } @@ -5759,8 +5764,7 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { } } - go sc.runHandler(rw, req, handler) - return nil + return sc.scheduleHandler(id, rw, req, handler) } func (sc *http2serverConn) upgradeRequest(req *Request) { @@ -5780,6 +5784,10 @@ func (sc *http2serverConn) upgradeRequest(req *Request) { sc.conn.SetReadDeadline(time.Time{}) } + // This is the first request on the connection, + // so start the handler directly rather than going + // through scheduleHandler. + sc.curHandlers++ go sc.runHandler(rw, req, sc.handler.ServeHTTP) } @@ -6021,8 +6029,62 @@ func (sc *http2serverConn) newResponseWriter(st *http2stream, req *Request) *htt return &http2responseWriter{rws: rws} } +type http2unstartedHandler struct { + streamID uint32 + rw *http2responseWriter + req *Request + handler func(ResponseWriter, *Request) +} + +// scheduleHandler starts a handler goroutine, +// or schedules one to start as soon as an existing handler finishes. +func (sc *http2serverConn) scheduleHandler(streamID uint32, rw *http2responseWriter, req *Request, handler func(ResponseWriter, *Request)) error { + sc.serveG.check() + maxHandlers := sc.advMaxStreams + if sc.curHandlers < maxHandlers { + sc.curHandlers++ + go sc.runHandler(rw, req, handler) + return nil + } + if len(sc.unstartedHandlers) > int(4*sc.advMaxStreams) { + return sc.countError("too_many_early_resets", http2ConnectionError(http2ErrCodeEnhanceYourCalm)) + } + sc.unstartedHandlers = append(sc.unstartedHandlers, http2unstartedHandler{ + streamID: streamID, + rw: rw, + req: req, + handler: handler, + }) + return nil +} + +func (sc *http2serverConn) handlerDone() { + sc.serveG.check() + sc.curHandlers-- + i := 0 + maxHandlers := sc.advMaxStreams + for ; i < len(sc.unstartedHandlers); i++ { + u := sc.unstartedHandlers[i] + if sc.streams[u.streamID] == nil { + // This stream was reset before its goroutine had a chance to start. + continue + } + if sc.curHandlers >= maxHandlers { + break + } + sc.curHandlers++ + go sc.runHandler(u.rw, u.req, u.handler) + sc.unstartedHandlers[i] = http2unstartedHandler{} // don't retain references + } + sc.unstartedHandlers = sc.unstartedHandlers[i:] + if len(sc.unstartedHandlers) == 0 { + sc.unstartedHandlers = nil + } +} + // Run on its own goroutine. func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, handler func(ResponseWriter, *Request)) { + defer sc.sendServeMsg(http2handlerDoneMsg) didPanic := true defer func() { rw.rws.stream.cancelCtx() From 482971896f61e892e185aeb543e3525e3a31ca6e Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Wed, 11 Oct 2023 14:18:50 +0200 Subject: [PATCH 2/2] chore: prepare for merging with go1.20.10 Part of https://github.com/ooni/probe/issues/2524 --- UPSTREAM | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPSTREAM b/UPSTREAM index 2cea1db3..64900e26 100644 --- a/UPSTREAM +++ b/UPSTREAM @@ -1 +1 @@ -go1.20.8 +go1.20.10