From 182336bf05c8e833f433510ba62205d8ce40299a Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 11 Aug 2025 10:21:50 -0700 Subject: [PATCH] net/http: fix data race in client Fixes #73522 Co-authored-by: Damien Neil Change-Id: I6fb408a0b03bc387f443e17e6f9d0bac32eff31e Reviewed-on: https://go-review.googlesource.com/c/go/+/694815 LUCI-TryBot-Result: Go LUCI Auto-Submit: Damien Neil Reviewed-by: Damien Neil Reviewed-by: David Chase --- src/net/http/transport.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 07b3a9e1e7..b860eb95b0 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -722,7 +722,7 @@ func (t *Transport) roundTrip(req *Request) (_ *Response, err error) { if e, ok := err.(transportReadFromServerError); ok { err = e.err } - if b, ok := req.Body.(*readTrackingBody); ok && !b.didClose { + if b, ok := req.Body.(*readTrackingBody); ok && !b.didClose.Load() { // Issue 49621: Close the request body if pconn.roundTrip // didn't do so already. This can happen if the pconn // write loop exits without reading the write request. @@ -752,8 +752,8 @@ var errCannotRewind = errors.New("net/http: cannot rewind body after connection type readTrackingBody struct { io.ReadCloser - didRead bool - didClose bool + didRead bool // not atomic.Bool because only one goroutine (the user's) should be accessing + didClose atomic.Bool } func (r *readTrackingBody) Read(data []byte) (int, error) { @@ -762,7 +762,9 @@ func (r *readTrackingBody) Read(data []byte) (int, error) { } func (r *readTrackingBody) Close() error { - r.didClose = true + if !r.didClose.CompareAndSwap(false, true) { + return nil + } return r.ReadCloser.Close() } @@ -784,10 +786,10 @@ func setupRewindBody(req *Request) *Request { // rewindBody takes care of closing req.Body when appropriate // (in all cases except when rewindBody returns req unmodified). func rewindBody(req *Request) (rewound *Request, err error) { - if req.Body == nil || req.Body == NoBody || (!req.Body.(*readTrackingBody).didRead && !req.Body.(*readTrackingBody).didClose) { + if req.Body == nil || req.Body == NoBody || (!req.Body.(*readTrackingBody).didRead && !req.Body.(*readTrackingBody).didClose.Load()) { return req, nil // nothing to rewind } - if !req.Body.(*readTrackingBody).didClose { + if !req.Body.(*readTrackingBody).didClose.Load() { req.closeBody() } if req.GetBody == nil { -- 2.51.0