Skip to content

Commit db576c9

Browse files
jupjgopherbot
authored andcommitted
net/textproto: initialize commonHeader in canonicalMIMEHeaderKey
Call initCommonHeader in canonicalMIMEHeaderKey to ensure that commonHeader is initialized before use. Remove all other calls to initCommonHeader, since commonHeader is only used in canonicalMIMEHeaderKey. This prevents a race condition: read of commonHeader before commonHeader has been initialized. Add regression test that triggers the race condition which can be detected by the race detector. Fixes #46363 Change-Id: I00c8c52c6f4c78c0305978c876142c1b388174af Reviewed-on: https://go-review.googlesource.com/c/go/+/397575 Trust: Brad Fitzpatrick <[email protected]> Reviewed-by: Bryan Mills <[email protected]> Trust: Bryan Mills <[email protected]> Run-TryBot: Bryan Mills <[email protected]> Auto-Submit: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 3a19102 commit db576c9

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

src/net/textproto/reader.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ type Reader struct {
2828
// should be reading from an io.LimitReader or similar Reader to bound
2929
// the size of responses.
3030
func NewReader(r *bufio.Reader) *Reader {
31-
commonHeaderOnce.Do(initCommonHeader)
3231
return &Reader{R: r}
3332
}
3433

@@ -579,8 +578,6 @@ func (r *Reader) upcomingHeaderNewlines() (n int) {
579578
// If s contains a space or invalid header field bytes, it is
580579
// returned without modifications.
581580
func CanonicalMIMEHeaderKey(s string) string {
582-
commonHeaderOnce.Do(initCommonHeader)
583-
584581
// Quick check for canonical encoding.
585582
upper := true
586583
for i := 0; i < len(s); i++ {
@@ -642,6 +639,7 @@ func canonicalMIMEHeaderKey(a []byte) string {
642639
a[i] = c
643640
upper = c == '-' // for next time
644641
}
642+
commonHeaderOnce.Do(initCommonHeader)
645643
// The compiler recognizes m[string(byteSlice)] as a special
646644
// case, so a copy of a's bytes into a new string does not
647645
// happen in this map lookup:

src/net/textproto/reader_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import (
88
"bufio"
99
"bytes"
1010
"io"
11+
"net"
1112
"reflect"
1213
"strings"
14+
"sync"
1315
"testing"
1416
)
1517

@@ -324,6 +326,33 @@ func TestCommonHeaders(t *testing.T) {
324326
}
325327
}
326328

329+
func TestIssue46363(t *testing.T) {
330+
// Regression test for data race reported in issue 46363:
331+
// ReadMIMEHeader reads commonHeader before commonHeader has been initialized.
332+
// Run this test with the race detector enabled to catch the reported data race.
333+
334+
// Reset commonHeaderOnce, so that commonHeader will have to be initialized
335+
commonHeaderOnce = sync.Once{}
336+
commonHeader = nil
337+
338+
// Test for data race by calling ReadMIMEHeader and CanonicalMIMEHeaderKey concurrently
339+
340+
// Send MIME header over net.Conn
341+
r, w := net.Pipe()
342+
go func() {
343+
// ReadMIMEHeader calls canonicalMIMEHeaderKey, which reads from commonHeader
344+
NewConn(r).ReadMIMEHeader()
345+
}()
346+
w.Write([]byte("A: 1\r\nB: 2\r\nC: 3\r\n\r\n"))
347+
348+
// CanonicalMIMEHeaderKey calls commonHeaderOnce.Do(initCommonHeader) which initializes commonHeader
349+
CanonicalMIMEHeaderKey("a")
350+
351+
if commonHeader == nil {
352+
t.Fatal("CanonicalMIMEHeaderKey should initialize commonHeader")
353+
}
354+
}
355+
327356
var clientHeaders = strings.Replace(`Host: golang.org
328357
Connection: keep-alive
329358
Cache-Control: max-age=0

0 commit comments

Comments
 (0)