Skip to content

net/http: panic due to racy read of persistConn after handler panic #46866

Closed
@neild

Description

@neild

Let's start with the reproduction case:

func TestHandlerAbortRacesBodyRead(t *testing.T) {
        setParallel(t)
        defer afterTest(t)

        ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
                go io.Copy(io.Discard, req.Body)
                panic(ErrAbortHandler)
        }))
        defer ts.Close()

        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, _ := NewRequest("POST", ts.URL, &io.LimitedReader{R: neverEnding('x'), N: reqLen})
                                req.ContentLength = reqLen
                                resp, _ := ts.Client().Transport.RoundTrip(req)
                                if resp != nil {
                                        resp.Body.Close()
                                }
                        }
                }()
        }
        wg.Wait()
}

There might be something simpler, but this consistently panics for me on multiple systems. The above is a bit contrived, but the panic consistently occurs in some much less contrived scenarios with httputil.ReverseProxy, which likes to panic(ErrAbortHandler) when it encounters a problem copying a proxied response body.

The root cause is (I think) that we don't close the request body after a handler panic. We do close the *conn, returning its bufio.Reader and bufio.Writer to their sync.Pools. But the request body has an indirect reference to the *conn's reader, and while the handler has exited, some other goroutine may still be reading from it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions