Skip to content

Commit f3529ca

Browse files
rsckatiehockman
authored andcommitted
[release-branch.go1.14-security] net/http: synchronize "100 Continue" write and Handler writes
The expectContinueReader writes to the connection on the first Request.Body read. Since a Handler might be doing a read in parallel or before a write, expectContinueReader needs to synchronize with the ResponseWriter, and abort if a response already went out. The tests will land in a separate CL. Fixes #34902 Fixes CVE-2020-15586 Change-Id: Icdd8dd539f45e8863762bd378194bb4741e875fc Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/793350 Reviewed-by: Filippo Valsorda <[email protected]> (cherry picked from commit b5e504f4a07c572744b228fa1b10e3989c4c44f3) Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/793500
1 parent 83b181c commit f3529ca

File tree

1 file changed

+36
-7
lines changed

1 file changed

+36
-7
lines changed

src/net/http/server.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,16 @@ type response struct {
425425
wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive"
426426
wantsClose bool // HTTP request has Connection "close"
427427

428+
// canWriteContinue is a boolean value accessed as an atomic int32
429+
// that says whether or not a 100 Continue header can be written
430+
// to the connection.
431+
// writeContinueMu must be held while writing the header.
432+
// These two fields together synchronize the body reader
433+
// (the expectContinueReader, which wants to write 100 Continue)
434+
// against the main writer.
435+
canWriteContinue atomicBool
436+
writeContinueMu sync.Mutex
437+
428438
w *bufio.Writer // buffers output in chunks to chunkWriter
429439
cw chunkWriter
430440

@@ -515,6 +525,7 @@ type atomicBool int32
515525

516526
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
517527
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
528+
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
518529

519530
// declareTrailer is called for each Trailer header when the
520531
// response header is written. It notes that a header will need to be
@@ -877,21 +888,27 @@ type expectContinueReader struct {
877888
resp *response
878889
readCloser io.ReadCloser
879890
closed bool
880-
sawEOF bool
891+
sawEOF atomicBool
881892
}
882893

883894
func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
884895
if ecr.closed {
885896
return 0, ErrBodyReadAfterClose
886897
}
887-
if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() {
888-
ecr.resp.wroteContinue = true
889-
ecr.resp.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
890-
ecr.resp.conn.bufw.Flush()
898+
w := ecr.resp
899+
if !w.wroteContinue && w.canWriteContinue.isSet() && !w.conn.hijacked() {
900+
w.wroteContinue = true
901+
w.writeContinueMu.Lock()
902+
if w.canWriteContinue.isSet() {
903+
w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
904+
w.conn.bufw.Flush()
905+
w.canWriteContinue.setFalse()
906+
}
907+
w.writeContinueMu.Unlock()
891908
}
892909
n, err = ecr.readCloser.Read(p)
893910
if err == io.EOF {
894-
ecr.sawEOF = true
911+
ecr.sawEOF.setTrue()
895912
}
896913
return
897914
}
@@ -1310,7 +1327,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
13101327
// because we don't know if the next bytes on the wire will be
13111328
// the body-following-the-timer or the subsequent request.
13121329
// See Issue 11549.
1313-
if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF {
1330+
if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.isSet() {
13141331
w.closeAfterReply = true
13151332
}
13161333

@@ -1560,6 +1577,17 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er
15601577
}
15611578
return 0, ErrHijacked
15621579
}
1580+
1581+
if w.canWriteContinue.isSet() {
1582+
// Body reader wants to write 100 Continue but hasn't yet.
1583+
// Tell it not to. The store must be done while holding the lock
1584+
// because the lock makes sure that there is not an active write
1585+
// this very moment.
1586+
w.writeContinueMu.Lock()
1587+
w.canWriteContinue.setFalse()
1588+
w.writeContinueMu.Unlock()
1589+
}
1590+
15631591
if !w.wroteHeader {
15641592
w.WriteHeader(StatusOK)
15651593
}
@@ -1871,6 +1899,7 @@ func (c *conn) serve(ctx context.Context) {
18711899
if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
18721900
// Wrap the Body reader with one that replies on the connection
18731901
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
1902+
w.canWriteContinue.setTrue()
18741903
}
18751904
} else if req.Header.get("Expect") != "" {
18761905
w.sendExpectationFailed()

0 commit comments

Comments
 (0)