Skip to content

Commit 3501186

Browse files
neildgopherbot
authored andcommitted
net/http: don't modify caller's tls.Config.NextProtos
Clone the input slice before adjusting NextProtos to add or remove "http/1.1" and "h2" entries, so as not to modify a slice that the caller might be using. (We clone the tls.Config that contains the slice, but that's a shallow clone.) Fixes #72100 Change-Id: I9f228b8fb6f6f2ca5023179ec114929c002dbda9 Reviewed-on: https://go-review.googlesource.com/c/go/+/654875 Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent a188955 commit 3501186

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

src/net/http/serve_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"compress/zlib"
1414
"context"
1515
"crypto/tls"
16+
"crypto/x509"
1617
"encoding/json"
1718
"errors"
1819
"fmt"
@@ -7335,3 +7336,71 @@ func TestInvalidChunkedBodies(t *testing.T) {
73357336
})
73367337
}
73377338
}
7339+
7340+
// Issue #72100: Verify that we don't modify the caller's TLS.Config.NextProtos slice.
7341+
func TestServerTLSNextProtos(t *testing.T) {
7342+
run(t, testServerTLSNextProtos, []testMode{https1Mode, http2Mode})
7343+
}
7344+
func testServerTLSNextProtos(t *testing.T, mode testMode) {
7345+
CondSkipHTTP2(t)
7346+
7347+
cert, err := tls.X509KeyPair(testcert.LocalhostCert, testcert.LocalhostKey)
7348+
if err != nil {
7349+
t.Fatal(err)
7350+
}
7351+
leafCert, err := x509.ParseCertificate(cert.Certificate[0])
7352+
if err != nil {
7353+
t.Fatal(err)
7354+
}
7355+
certpool := x509.NewCertPool()
7356+
certpool.AddCert(leafCert)
7357+
7358+
protos := new(Protocols)
7359+
switch mode {
7360+
case https1Mode:
7361+
protos.SetHTTP1(true)
7362+
case http2Mode:
7363+
protos.SetHTTP2(true)
7364+
}
7365+
7366+
wantNextProtos := []string{"http/1.1", "h2", "other"}
7367+
nextProtos := slices.Clone(wantNextProtos)
7368+
7369+
// We don't use httptest here because it overrides the tls.Config.
7370+
srv := &Server{
7371+
TLSConfig: &tls.Config{
7372+
Certificates: []tls.Certificate{cert},
7373+
NextProtos: nextProtos,
7374+
},
7375+
Handler: HandlerFunc(func(w ResponseWriter, req *Request) {}),
7376+
Protocols: protos,
7377+
}
7378+
tr := &Transport{
7379+
TLSClientConfig: &tls.Config{
7380+
RootCAs: certpool,
7381+
NextProtos: nextProtos,
7382+
},
7383+
Protocols: protos,
7384+
}
7385+
7386+
listener := newLocalListener(t)
7387+
srvc := make(chan error, 1)
7388+
go func() {
7389+
srvc <- srv.ServeTLS(listener, "", "")
7390+
}()
7391+
t.Cleanup(func() {
7392+
srv.Close()
7393+
<-srvc
7394+
})
7395+
7396+
client := &Client{Transport: tr}
7397+
resp, err := client.Get("https://" + listener.Addr().String())
7398+
if err != nil {
7399+
t.Fatal(err)
7400+
}
7401+
resp.Body.Close()
7402+
7403+
if !slices.Equal(nextProtos, wantNextProtos) {
7404+
t.Fatalf("after running test: original NextProtos slice = %v, want %v", nextProtos, wantNextProtos)
7405+
}
7406+
}

src/net/http/server.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,12 @@ func (s *Server) protocols() Protocols {
35213521
// adjustNextProtos adds or removes "http/1.1" and "h2" entries from
35223522
// a tls.Config.NextProtos list, according to the set of protocols in protos.
35233523
func adjustNextProtos(nextProtos []string, protos Protocols) []string {
3524+
// Make a copy of NextProtos since it might be shared with some other tls.Config.
3525+
// (tls.Config.Clone doesn't do a deep copy.)
3526+
//
3527+
// We could avoid an allocation in the common case by checking to see if the slice
3528+
// is already in order, but this is just one small allocation per connection.
3529+
nextProtos = slices.Clone(nextProtos)
35243530
var have Protocols
35253531
nextProtos = slices.DeleteFunc(nextProtos, func(s string) bool {
35263532
switch s {

0 commit comments

Comments
 (0)