Skip to content

Commit 3203a5d

Browse files
neildgopherbot
authored andcommitted
net/http: avoid connCount underflow race
Remove a race condition in counting the number of connections per host, which can cause a connCount underflow and a panic. The race occurs when: - A RoundTrip call attempts to use a HTTP/2 roundtripper (pconn.alt != nil) and receives an isNoCachedConn error. The call removes the pconn from the idle conn pool and decrements the connCount for its host. - A second RoundTrip call on the same pconn succeeds, and delivers the pconn to a third RoundTrip waiting for a conn. - The third RoundTrip receives the pconn at the same moment its request context is canceled. It places the pconn back into the idle conn pool. At this time, the connCount is incorrect, because the conn returned to the idle pool is not matched by an increment in the connCount. Fix this by not adding HTTP/2 pconns back to the idle pool in wantConn.cancel. Fixes golang#61474 Change-Id: I104d6cf85a54d0382eebf3fcf5dda99c69a7c3f6 Reviewed-on: https://go-review.googlesource.com/c/go/+/703936 Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Nicholas Husin <[email protected]> Reviewed-by: Nicholas Husin <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 8ca209e commit 3203a5d

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

src/net/http/transport.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,10 @@ func (w *wantConn) cancel(t *Transport) {
13821382
w.done = true
13831383
w.mu.Unlock()
13841384

1385-
if pc != nil {
1385+
// HTTP/2 connections (pc.alt != nil) aren't removed from the idle pool on use,
1386+
// and should not be added back here. If the pconn isn't in the idle pool,
1387+
// it's because we removed it due to an error.
1388+
if pc != nil && pc.alt == nil {
13861389
t.putOrCloseIdleConn(pc)
13871390
}
13881391
}

src/net/http/transport_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7625,3 +7625,35 @@ func TestTransportServerProtocols(t *testing.T) {
76257625
})
76267626
}
76277627
}
7628+
7629+
func TestIssue61474(t *testing.T) {
7630+
run(t, testIssue61474, []testMode{http2Mode})
7631+
}
7632+
func testIssue61474(t *testing.T, mode testMode) {
7633+
if testing.Short() {
7634+
return
7635+
}
7636+
7637+
// This test reliably exercises the condition causing #61474,
7638+
// but requires many iterations to do so.
7639+
// Keep the test around for now, but don't run it by default.
7640+
t.Skip("test is too large")
7641+
7642+
cst := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
7643+
}), func(tr *Transport) {
7644+
tr.MaxConnsPerHost = 1
7645+
})
7646+
var wg sync.WaitGroup
7647+
defer wg.Wait()
7648+
for range 100000 {
7649+
wg.Go(func() {
7650+
ctx, cancel := context.WithTimeout(t.Context(), 1*time.Millisecond)
7651+
defer cancel()
7652+
req, _ := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil)
7653+
resp, err := cst.c.Do(req)
7654+
if err == nil {
7655+
resp.Body.Close()
7656+
}
7657+
})
7658+
}
7659+
}

0 commit comments

Comments
 (0)