Skip to content

Commit 0e37f52

Browse files
1 parent 7e1f8d9 commit 0e37f52

File tree

1 file changed

+331
-0
lines changed

1 file changed

+331
-0
lines changed
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
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

Comments
 (0)