diff options
author | Damien Neil <dneil@google.com> | 2021-07-07 16:34:34 -0700 |
---|---|---|
committer | Filippo Valsorda <filippo@golang.org> | 2021-07-30 14:01:30 +0000 |
commit | b7a85e0003cedb1b48a1fd3ae5b746ec6330102e (patch) | |
tree | fe86fc80397496bbb065a7a1ae55799111b240b2 /src/net/http/httputil/reverseproxy_test.go | |
parent | 70fd4e47d73b92fe90e44ac785e2f98f9df0ab67 (diff) | |
download | go-b7a85e0003cedb1b48a1fd3ae5b746ec6330102e.tar.gz go-b7a85e0003cedb1b48a1fd3ae5b746ec6330102e.zip |
net/http/httputil: close incoming ReverseProxy request body
Reading from an incoming request body after the request handler aborts
with a panic can cause a panic, becuse http.Server does not (contrary
to its documentation) close the request body in this case.
Always close the incoming request body in ReverseProxy.ServeHTTP to
ensure that any in-flight outgoing requests using the body do not
read from it.
Updates #46866
Fixes CVE-2021-36221
Change-Id: I310df269200ad8732c5d9f1a2b00de68725831df
Reviewed-on: https://go-review.googlesource.com/c/go/+/333191
Trust: Damien Neil <dneil@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Diffstat (limited to 'src/net/http/httputil/reverseproxy_test.go')
-rw-r--r-- | src/net/http/httputil/reverseproxy_test.go | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go index 1898ed8b8a..4b6ad77a29 100644 --- a/src/net/http/httputil/reverseproxy_test.go +++ b/src/net/http/httputil/reverseproxy_test.go @@ -1122,6 +1122,45 @@ func TestReverseProxy_PanicBodyError(t *testing.T) { rproxy.ServeHTTP(httptest.NewRecorder(), req) } +// Issue #46866: panic without closing incoming request body causes a panic +func TestReverseProxy_PanicClosesIncomingBody(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + out := "this call was relayed by the reverse proxy" + // Coerce a wrong content length to induce io.ErrUnexpectedEOF + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out)*2)) + fmt.Fprintln(w, out) + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := NewSingleHostReverseProxy(backendURL) + proxyHandler.ErrorLog = log.New(io.Discard, "", 0) // quiet for tests + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + frontendClient := frontend.Client() + + var wg sync.WaitGroup + for i := 0; i < 2; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < 10; j++ { + const reqLen = 6 * 1024 * 1024 + req, _ := http.NewRequest("POST", frontend.URL, &io.LimitedReader{R: neverEnding('x'), N: reqLen}) + req.ContentLength = reqLen + resp, _ := frontendClient.Transport.RoundTrip(req) + if resp != nil { + io.Copy(io.Discard, resp.Body) + resp.Body.Close() + } + } + }() + } + wg.Wait() +} + func TestSelectFlushInterval(t *testing.T) { tests := []struct { name string |