Skip to content

Commit fa98f46

Browse files
rsckatiehockman
authored andcommitted
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]> Reviewed-on: https://go-review.googlesource.com/c/go/+/242598 Run-TryBot: Katie Hockman <[email protected]> Reviewed-by: Filippo Valsorda <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 82175e6 commit fa98f46

File tree

1 file changed

+36
-7
lines changed

1 file changed

+36
-7
lines changed

src/net/http/server.go

+36-7
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
@@ -878,21 +889,27 @@ type expectContinueReader struct {
878889
resp *response
879890
readCloser io.ReadCloser
880891
closed bool
881-
sawEOF bool
892+
sawEOF atomicBool
882893
}
883894

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

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

0 commit comments

Comments
 (0)