From c2db5f4ccc61ba7df96a747e268a277b802cbb87 Mon Sep 17 00:00:00 2001 From: Jed Denlea Date: Wed, 29 Jul 2015 18:10:32 -0700 Subject: net/http: close server conn after request body error HTTP servers attempt to entirely consume a request body before sending a response. However, when doing so, it previously would ignore any errors encountered. Unfortunately, the errors triggered at this stage are indicative of at least a couple problems: read timeouts and chunked encoding errors. This means properly crafted and/or timed requests could lead to a "smuggled" request. The fix is to inspect the errors created by the response body Reader, and treat anything other than io.EOF or ErrBodyReadAfterClose as fatal to the connection. Fixes #11930 Change-Id: I0bf18006d7d8f6537529823fc450f2e2bdb7c18e Reviewed-on: https://go-review.googlesource.com/12865 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/net/http/server.go | 59 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 13 deletions(-) (limited to 'src/net/http/server.go') diff --git a/src/net/http/server.go b/src/net/http/server.go index d12f696eaf..905a8b9ad8 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -872,23 +872,56 @@ func (cw *chunkWriter) writeHeader(p []byte) { // don't want to do an unbounded amount of reading here for // DoS reasons, so we only try up to a threshold. if w.req.ContentLength != 0 && !w.closeAfterReply { - ecr, isExpecter := w.req.Body.(*expectContinueReader) - if !isExpecter || ecr.resp.wroteContinue { - var tooBig bool - if reqBody, ok := w.req.Body.(*body); ok && reqBody.unreadDataSize() >= maxPostHandlerReadBytes { + var discard, tooBig bool + + switch bdy := w.req.Body.(type) { + case *expectContinueReader: + if bdy.resp.wroteContinue { + discard = true + } + case *body: + switch { + case bdy.closed: + if !bdy.sawEOF { + // Body was closed in handler with non-EOF error. + w.closeAfterReply = true + } + case bdy.unreadDataSize() >= maxPostHandlerReadBytes: tooBig = true - } else { - n, _ := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1) - tooBig = n >= maxPostHandlerReadBytes + default: + discard = true } - if tooBig { - w.requestTooLarge() - delHeader("Connection") - setHeader.connection = "close" - } else { - w.req.Body.Close() + default: + discard = true + } + + if discard { + _, err := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1) + switch err { + case nil: + // There must be even more data left over. + tooBig = true + case ErrBodyReadAfterClose: + // Body was already consumed and closed. + case io.EOF: + // The remaining body was just consumed, close it. + err = w.req.Body.Close() + if err != nil { + w.closeAfterReply = true + } + default: + // Some other kind of error occured, like a read timeout, or + // corrupt chunked encoding. In any case, whatever remains + // on the wire must not be parsed as another HTTP request. + w.closeAfterReply = true } } + + if tooBig { + w.requestTooLarge() + delHeader("Connection") + setHeader.connection = "close" + } } code := w.status -- cgit v1.3