Skip to content

Commit e434185

Browse files
rsckatiehockman
authored andcommitted
[release-branch.go1.13-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/+/793499
1 parent 6be4a5e commit e434185

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
@@ -424,6 +424,16 @@ type response struct {
424424
wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive"
425425
wantsClose bool // HTTP request has Connection "close"
426426

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

@@ -514,6 +524,7 @@ type atomicBool int32
514524

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

518529
// declareTrailer is called for each Trailer header when the
519530
// response header is written. It notes that a header will need to be
@@ -876,21 +887,27 @@ type expectContinueReader struct {
876887
resp *response
877888
readCloser io.ReadCloser
878889
closed bool
879-
sawEOF bool
890+
sawEOF atomicBool
880891
}
881892

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

@@ -1554,6 +1571,17 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er
15541571
}
15551572
return 0, ErrHijacked
15561573
}
1574+
1575+
if w.canWriteContinue.isSet() {
1576+
// Body reader wants to write 100 Continue but hasn't yet.
1577+
// Tell it not to. The store must be done while holding the lock
1578+
// because the lock makes sure that there is not an active write
1579+
// this very moment.
1580+
w.writeContinueMu.Lock()
1581+
w.canWriteContinue.setFalse()
1582+
w.writeContinueMu.Unlock()
1583+
}
1584+
15571585
if !w.wroteHeader {
15581586
w.WriteHeader(StatusOK)
15591587
}
@@ -1866,6 +1894,7 @@ func (c *conn) serve(ctx context.Context) {
18661894
if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
18671895
// Wrap the Body reader with one that replies on the connection
18681896
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
1897+
w.canWriteContinue.setTrue()
18691898
}
18701899
} else if req.Header.get("Expect") != "" {
18711900
w.sendExpectationFailed()

0 commit comments

Comments
 (0)