|
| 1 | +From a5e9aab67d3c4ff3ded35495ae03fa34f12e950b Mon Sep 17 00:00:00 2001 |
| 2 | +From: Archana Ravindar < [email protected]> |
| 3 | +Date: Mon, 12 Aug 2024 19:40:21 +0530 |
| 4 | +Subject: [PATCH] - backport fix for CVE-2024-24791 to Go1.19 - |
| 5 | + https://go-review.googlesource.com/c/go/+/595096 |
| 6 | + |
| 7 | +--- |
| 8 | + src/net/http/server.go | 25 ++-- |
| 9 | + src/net/http/transport.go | 35 ++++-- |
| 10 | + src/net/http/transport_test.go | 205 ++++++++++++++++++++------------- |
| 11 | + 3 files changed, 166 insertions(+), 99 deletions(-) |
| 12 | + |
| 13 | +diff --git a/src/net/http/server.go b/src/net/http/server.go |
| 14 | +index 87dd412984..fdcc3a9136 100644 |
| 15 | +--- a/src/net/http/server.go |
| 16 | ++++ b/src/net/http/server.go |
| 17 | +@@ -1344,16 +1344,21 @@ func (cw *chunkWriter) writeHeader(p []byte) { |
| 18 | + |
| 19 | + // If the client wanted a 100-continue but we never sent it to |
| 20 | + // them (or, more strictly: we never finished reading their |
| 21 | +- // request body), don't reuse this connection because it's now |
| 22 | +- // in an unknown state: we might be sending this response at |
| 23 | +- // the same time the client is now sending its request body |
| 24 | +- // after a timeout. (Some HTTP clients send Expect: |
| 25 | +- // 100-continue but knowing that some servers don't support |
| 26 | +- // it, the clients set a timer and send the body later anyway) |
| 27 | +- // If we haven't seen EOF, we can't skip over the unread body |
| 28 | +- // because we don't know if the next bytes on the wire will be |
| 29 | +- // the body-following-the-timer or the subsequent request. |
| 30 | +- // See Issue 11549. |
| 31 | ++ // request body), don't reuse this connection. |
| 32 | ++ // |
| 33 | ++ // This behavior was first added on the theory that we don't know |
| 34 | ++ // if the next bytes on the wire are going to be the remainder of |
| 35 | ++ // the request body or the subsequent request (see issue 11549), |
| 36 | ++ // but that's not correct: If we keep using the connection, |
| 37 | ++ // the client is required to send the request body whether we |
| 38 | ++ // asked for it or not. |
| 39 | ++ // |
| 40 | ++ // We probably do want to skip reusing the connection in most cases, |
| 41 | ++ // however. If the client is offering a large request body that we |
| 42 | ++ // don't intend to use, then it's better to close the connection |
| 43 | ++ // than to read the body. For now, assume that if we're sending |
| 44 | ++ // headers, the handler is done reading the body and we should |
| 45 | ++ // drop the connection if we haven't seen EOF. |
| 46 | + if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.isSet() { |
| 47 | + w.closeAfterReply = true |
| 48 | + } |
| 49 | +diff --git a/src/net/http/transport.go b/src/net/http/transport.go |
| 50 | +index e470a6c080..083c907184 100644 |
| 51 | +--- a/src/net/http/transport.go |
| 52 | ++++ b/src/net/http/transport.go |
| 53 | +@@ -2288,17 +2288,12 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr |
| 54 | + return |
| 55 | + } |
| 56 | + resCode := resp.StatusCode |
| 57 | +- if continueCh != nil { |
| 58 | +- if resCode == 100 { |
| 59 | +- if trace != nil && trace.Got100Continue != nil { |
| 60 | +- trace.Got100Continue() |
| 61 | +- } |
| 62 | +- continueCh <- struct{}{} |
| 63 | +- continueCh = nil |
| 64 | +- } else if resCode >= 200 { |
| 65 | +- close(continueCh) |
| 66 | +- continueCh = nil |
| 67 | ++ if continueCh != nil && resCode == StatusContinue { |
| 68 | ++ if trace != nil && trace.Got100Continue != nil { |
| 69 | ++ trace.Got100Continue() |
| 70 | + } |
| 71 | ++ continueCh <- struct{}{} |
| 72 | ++ continueCh = nil |
| 73 | + } |
| 74 | + is1xx := 100 <= resCode && resCode <= 199 |
| 75 | + // treat 101 as a terminal status, see issue 26161 |
| 76 | +@@ -2322,6 +2317,26 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr |
| 77 | + resp.Body = newReadWriteCloserBody(pc.br, pc.conn) |
| 78 | + } |
| 79 | + |
| 80 | ++ if continueCh != nil { |
| 81 | ++ // We send an "Expect: 100-continue" header, but the server |
| 82 | ++ // responded with a terminal status and no 100 Continue. |
| 83 | ++ // |
| 84 | ++ // If we're going to keep using the connection, we need to send the request body. |
| 85 | ++ // Tell writeLoop to skip sending the body if we're going to close the connection, |
| 86 | ++ // or to send it otherwise. |
| 87 | ++ // |
| 88 | ++ // The case where we receive a 101 Switching Protocols response is a bit |
| 89 | ++ // ambiguous, since we don't know what protocol we're switching to. |
| 90 | ++ // Conceivably, it's one that doesn't need us to send the body. |
| 91 | ++ // Given that we'll send the body if ExpectContinueTimeout expires, |
| 92 | ++ // be consistent and always send it if we aren't closing the connection. |
| 93 | ++ if resp.Close || rc.req.Close { |
| 94 | ++ close(continueCh) // don't send the body; the connection will close |
| 95 | ++ } else { |
| 96 | ++ continueCh <- struct{}{} // send the body |
| 97 | ++ } |
| 98 | ++ } |
| 99 | ++ |
| 100 | + resp.TLS = pc.tlsState |
| 101 | + return |
| 102 | + } |
| 103 | +diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go |
| 104 | +index 985d0625dc..63bcf0384b 100644 |
| 105 | +--- a/src/net/http/transport_test.go |
| 106 | ++++ b/src/net/http/transport_test.go |
| 107 | +@@ -1150,95 +1150,142 @@ func TestTransportGzip(t *testing.T) { |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | +-// If a request has Expect:100-continue header, the request blocks sending body until the first response. |
| 112 | +-// Premature consumption of the request body should not be occurred. |
| 113 | +-func TestTransportExpect100Continue(t *testing.T) { |
| 114 | +- setParallel(t) |
| 115 | +- defer afterTest(t) |
| 116 | ++// A transport100Continue test exercises Transport behaviors when sending a |
| 117 | ++// request with an Expect: 100-continue header. |
| 118 | ++type transport100ContinueTest struct { |
| 119 | ++ t *testing.T |
| 120 | + |
| 121 | +- ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { |
| 122 | +- switch req.URL.Path { |
| 123 | +- case "/100": |
| 124 | +- // This endpoint implicitly responds 100 Continue and reads body. |
| 125 | +- if _, err := io.Copy(io.Discard, req.Body); err != nil { |
| 126 | +- t.Error("Failed to read Body", err) |
| 127 | +- } |
| 128 | +- rw.WriteHeader(StatusOK) |
| 129 | +- case "/200": |
| 130 | +- // Go 1.5 adds Connection: close header if the client expect |
| 131 | +- // continue but not entire request body is consumed. |
| 132 | +- rw.WriteHeader(StatusOK) |
| 133 | +- case "/500": |
| 134 | +- rw.WriteHeader(StatusInternalServerError) |
| 135 | +- case "/keepalive": |
| 136 | +- // This hijacked endpoint responds error without Connection:close. |
| 137 | +- _, bufrw, err := rw.(Hijacker).Hijack() |
| 138 | +- if err != nil { |
| 139 | +- log.Fatal(err) |
| 140 | +- } |
| 141 | +- bufrw.WriteString("HTTP/1.1 500 Internal Server Error\r\n") |
| 142 | +- bufrw.WriteString("Content-Length: 0\r\n\r\n") |
| 143 | +- bufrw.Flush() |
| 144 | +- case "/timeout": |
| 145 | +- // This endpoint tries to read body without 100 (Continue) response. |
| 146 | +- // After ExpectContinueTimeout, the reading will be started. |
| 147 | +- conn, bufrw, err := rw.(Hijacker).Hijack() |
| 148 | +- if err != nil { |
| 149 | +- log.Fatal(err) |
| 150 | +- } |
| 151 | +- if _, err := io.CopyN(io.Discard, bufrw, req.ContentLength); err != nil { |
| 152 | +- t.Error("Failed to read Body", err) |
| 153 | +- } |
| 154 | +- bufrw.WriteString("HTTP/1.1 200 OK\r\n\r\n") |
| 155 | +- bufrw.Flush() |
| 156 | +- conn.Close() |
| 157 | +- } |
| 158 | ++ reqdone chan struct{} |
| 159 | ++ resp *Response |
| 160 | ++ respErr error |
| 161 | + |
| 162 | +- })) |
| 163 | +- defer ts.Close() |
| 164 | ++ conn net.Conn |
| 165 | ++ reader *bufio.Reader |
| 166 | ++} |
| 167 | + |
| 168 | +- tests := []struct { |
| 169 | +- path string |
| 170 | +- body []byte |
| 171 | +- sent int |
| 172 | +- status int |
| 173 | +- }{ |
| 174 | +- {path: "/100", body: []byte("hello"), sent: 5, status: 200}, // Got 100 followed by 200, entire body is sent. |
| 175 | +- {path: "/200", body: []byte("hello"), sent: 0, status: 200}, // Got 200 without 100. body isn't sent. |
| 176 | +- {path: "/500", body: []byte("hello"), sent: 0, status: 500}, // Got 500 without 100. body isn't sent. |
| 177 | +- {path: "/keepalive", body: []byte("hello"), sent: 0, status: 500}, // Although without Connection:close, body isn't sent. |
| 178 | +- {path: "/timeout", body: []byte("hello"), sent: 5, status: 200}, // Timeout exceeded and entire body is sent. |
| 179 | ++const transport100ContinueTestBody = "request body" |
| 180 | ++ |
| 181 | ++// newTransport100ContinueTest creates a Transport and sends an Expect: 100-continue |
| 182 | ++// request on it. |
| 183 | ++func newTransport100ContinueTest(t *testing.T, timeout time.Duration) *transport100ContinueTest { |
| 184 | ++ ln := newLocalListener(t) |
| 185 | ++ defer ln.Close() |
| 186 | ++ |
| 187 | ++ test := &transport100ContinueTest{ |
| 188 | ++ t: t, |
| 189 | ++ reqdone: make(chan struct{}), |
| 190 | + } |
| 191 | + |
| 192 | +- c := ts.Client() |
| 193 | +- for i, v := range tests { |
| 194 | +- tr := &Transport{ |
| 195 | +- ExpectContinueTimeout: 2 * time.Second, |
| 196 | +- } |
| 197 | +- defer tr.CloseIdleConnections() |
| 198 | +- c.Transport = tr |
| 199 | +- body := bytes.NewReader(v.body) |
| 200 | +- req, err := NewRequest("PUT", ts.URL+v.path, body) |
| 201 | +- if err != nil { |
| 202 | +- t.Fatal(err) |
| 203 | +- } |
| 204 | +- req.Header.Set("Expect", "100-continue") |
| 205 | +- req.ContentLength = int64(len(v.body)) |
| 206 | ++ tr := &Transport{ |
| 207 | ++ ExpectContinueTimeout: timeout, |
| 208 | ++ } |
| 209 | ++ go func() { |
| 210 | ++ defer close(test.reqdone) |
| 211 | ++ body := strings.NewReader(transport100ContinueTestBody) |
| 212 | ++ req, _ := NewRequest("PUT", "http://"+ln.Addr().String(), body) |
| 213 | ++ req.Header.Set("Expect", "100-continue") |
| 214 | ++ req.ContentLength = int64(len(transport100ContinueTestBody)) |
| 215 | ++ test.resp, test.respErr = tr.RoundTrip(req) |
| 216 | ++ test.resp.Body.Close() |
| 217 | ++ }() |
| 218 | + |
| 219 | +- resp, err := c.Do(req) |
| 220 | +- if err != nil { |
| 221 | +- t.Fatal(err) |
| 222 | ++ c, err := ln.Accept() |
| 223 | ++ if err != nil { |
| 224 | ++ t.Fatalf("Accept: %v", err) |
| 225 | ++ } |
| 226 | ++ t.Cleanup(func() { |
| 227 | ++ c.Close() |
| 228 | ++ }) |
| 229 | ++ br := bufio.NewReader(c) |
| 230 | ++ _, err = ReadRequest(br) |
| 231 | ++ if err != nil { |
| 232 | ++ t.Fatalf("ReadRequest: %v", err) |
| 233 | ++ } |
| 234 | ++ test.conn = c |
| 235 | ++ test.reader = br |
| 236 | ++ t.Cleanup(func() { |
| 237 | ++ <-test.reqdone |
| 238 | ++ tr.CloseIdleConnections() |
| 239 | ++ got, _ := io.ReadAll(test.reader) |
| 240 | ++ if len(got) > 0 { |
| 241 | ++ t.Fatalf("Transport sent unexpected bytes: %q", got) |
| 242 | + } |
| 243 | +- resp.Body.Close() |
| 244 | ++ }) |
| 245 | + |
| 246 | +- sent := len(v.body) - body.Len() |
| 247 | +- if v.status != resp.StatusCode { |
| 248 | +- t.Errorf("test %d: status code should be %d but got %d. (%s)", i, v.status, resp.StatusCode, v.path) |
| 249 | +- } |
| 250 | +- if v.sent != sent { |
| 251 | +- t.Errorf("test %d: sent body should be %d but sent %d. (%s)", i, v.sent, sent, v.path) |
| 252 | ++ return test |
| 253 | ++} |
| 254 | ++ |
| 255 | ++// respond sends response lines from the server to the transport. |
| 256 | ++func (test *transport100ContinueTest) respond(lines ...string) { |
| 257 | ++ for _, line := range lines { |
| 258 | ++ if _, err := test.conn.Write([]byte(line + "\r\n")); err != nil { |
| 259 | ++ test.t.Fatalf("Write: %v", err) |
| 260 | + } |
| 261 | + } |
| 262 | ++ if _, err := test.conn.Write([]byte("\r\n")); err != nil { |
| 263 | ++ test.t.Fatalf("Write: %v", err) |
| 264 | ++ } |
| 265 | ++} |
| 266 | ++ |
| 267 | ++// wantBodySent ensures the transport has sent the request body to the server. |
| 268 | ++func (test *transport100ContinueTest) wantBodySent() { |
| 269 | ++ got, err := io.ReadAll(io.LimitReader(test.reader, int64(len(transport100ContinueTestBody)))) |
| 270 | ++ if err != nil { |
| 271 | ++ test.t.Fatalf("unexpected error reading body: %v", err) |
| 272 | ++ } |
| 273 | ++ if got, want := string(got), transport100ContinueTestBody; got != want { |
| 274 | ++ test.t.Fatalf("unexpected body: got %q, want %q", got, want) |
| 275 | ++ } |
| 276 | ++} |
| 277 | ++ |
| 278 | ++// wantRequestDone ensures the Transport.RoundTrip has completed with the expected status. |
| 279 | ++func (test *transport100ContinueTest) wantRequestDone(want int) { |
| 280 | ++ <-test.reqdone |
| 281 | ++ if test.respErr != nil { |
| 282 | ++ test.t.Fatalf("unexpected RoundTrip error: %v", test.respErr) |
| 283 | ++ } |
| 284 | ++ if got := test.resp.StatusCode; got != want { |
| 285 | ++ test.t.Fatalf("unexpected response code: got %v, want %v", got, want) |
| 286 | ++ } |
| 287 | ++} |
| 288 | ++ |
| 289 | ++func TestTransportExpect100ContinueSent(t *testing.T) { |
| 290 | ++ test := newTransport100ContinueTest(t, 1*time.Hour) |
| 291 | ++ // Server sends a 100 Continue response, and the client sends the request body. |
| 292 | ++ test.respond("HTTP/1.1 100 Continue") |
| 293 | ++ test.wantBodySent() |
| 294 | ++ test.respond("HTTP/1.1 200", "Content-Length: 0") |
| 295 | ++ test.wantRequestDone(200) |
| 296 | ++} |
| 297 | ++ |
| 298 | ++func TestTransportExpect100Continue200ResponseNoConnClose(t *testing.T) { |
| 299 | ++ test := newTransport100ContinueTest(t, 1*time.Hour) |
| 300 | ++ // No 100 Continue response, no Connection: close header. |
| 301 | ++ test.respond("HTTP/1.1 200", "Content-Length: 0") |
| 302 | ++ test.wantBodySent() |
| 303 | ++ test.wantRequestDone(200) |
| 304 | ++} |
| 305 | ++ |
| 306 | ++func TestTransportExpect100Continue200ResponseWithConnClose(t *testing.T) { |
| 307 | ++ test := newTransport100ContinueTest(t, 1*time.Hour) |
| 308 | ++ // No 100 Continue response, Connection: close header set. |
| 309 | ++ test.respond("HTTP/1.1 200", "Connection: close", "Content-Length: 0") |
| 310 | ++ test.wantRequestDone(200) |
| 311 | ++} |
| 312 | ++ |
| 313 | ++func TestTransportExpect100Continue500ResponseNoConnClose(t *testing.T) { |
| 314 | ++ test := newTransport100ContinueTest(t, 1*time.Hour) |
| 315 | ++ // No 100 Continue response, no Connection: close header. |
| 316 | ++ test.respond("HTTP/1.1 500", "Content-Length: 0") |
| 317 | ++ test.wantBodySent() |
| 318 | ++ test.wantRequestDone(500) |
| 319 | ++} |
| 320 | ++ |
| 321 | ++func TestTransportExpect100Continue500ResponseTimeout(t *testing.T) { |
| 322 | ++ test := newTransport100ContinueTest(t, 5*time.Millisecond) // short timeout |
| 323 | ++ test.wantBodySent() // after timeout |
| 324 | ++ test.respond("HTTP/1.1 200", "Content-Length: 0") |
| 325 | ++ test.wantRequestDone(200) |
| 326 | + } |
| 327 | + |
| 328 | + func TestSOCKS5Proxy(t *testing.T) { |
| 329 | +-- |
| 330 | +2.39.3 |
| 331 | + |
0 commit comments