!387 [sync] PR-385: [Backport]fix CVE-2024-24791
From: @openeuler-sync-bot Reviewed-by: @fuowang Signed-off-by: @fuowang
This commit is contained in:
commit
8d4493a085
@ -0,0 +1,353 @@
|
|||||||
|
From c9be6ae748b7679b644a38182d456cb5a6ac06ee Mon Sep 17 00:00:00 2001
|
||||||
|
From: Damien Neil <dneil@google.com>
|
||||||
|
Date: Thu, 6 Jun 2024 12:50:46 -0700
|
||||||
|
Subject: [PATCH] [release-branch.go1.21] net/http: send body or close
|
||||||
|
connection on expect-100-continue requests
|
||||||
|
|
||||||
|
When sending a request with an "Expect: 100-continue" header,
|
||||||
|
we must send the request body before sending any further requests
|
||||||
|
on the connection.
|
||||||
|
|
||||||
|
When receiving a non-1xx response to an "Expect: 100-continue" request,
|
||||||
|
send the request body if the connection isn't being closed after
|
||||||
|
processing the response. In other words, if either the request
|
||||||
|
or response contains a "Connection: close" header, then skip sending
|
||||||
|
the request body (because the connection will not be used for
|
||||||
|
further requests), but otherwise send it.
|
||||||
|
|
||||||
|
Correct a comment on the server-side Expect: 100-continue handling
|
||||||
|
that implied sending the request body is optional. It isn't.
|
||||||
|
|
||||||
|
For #67555
|
||||||
|
Fixes #68199
|
||||||
|
|
||||||
|
Change-Id: Ia2f12091bee697771087f32ac347509ec5922d54
|
||||||
|
Reviewed-on: https://go-review.googlesource.com/c/go/+/591255
|
||||||
|
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
|
||||||
|
Reviewed-by: Jonathan Amsterdam <jba@google.com>
|
||||||
|
(cherry picked from commit cf501e05e138e6911f759a5db786e90b295499b9)
|
||||||
|
Reviewed-on: https://go-review.googlesource.com/c/go/+/595096
|
||||||
|
Reviewed-by: Joedian Reid <joedian@google.com>
|
||||||
|
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
|
||||||
|
---
|
||||||
|
src/net/http/server.go | 25 ++--
|
||||||
|
src/net/http/transport.go | 34 ++++--
|
||||||
|
src/net/http/transport_test.go | 202 ++++++++++++++++++++-------------
|
||||||
|
3 files changed, 164 insertions(+), 97 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/net/http/server.go b/src/net/http/server.go
|
||||||
|
index 8f63a90299..111adb0ecd 100644
|
||||||
|
--- a/src/net/http/server.go
|
||||||
|
+++ b/src/net/http/server.go
|
||||||
|
@@ -1352,16 +1352,21 @@ func (cw *chunkWriter) writeHeader(p []byte) {
|
||||||
|
|
||||||
|
// If the client wanted a 100-continue but we never sent it to
|
||||||
|
// them (or, more strictly: we never finished reading their
|
||||||
|
- // request body), don't reuse this connection because it's now
|
||||||
|
- // in an unknown state: we might be sending this response at
|
||||||
|
- // the same time the client is now sending its request body
|
||||||
|
- // after a timeout. (Some HTTP clients send Expect:
|
||||||
|
- // 100-continue but knowing that some servers don't support
|
||||||
|
- // it, the clients set a timer and send the body later anyway)
|
||||||
|
- // If we haven't seen EOF, we can't skip over the unread body
|
||||||
|
- // because we don't know if the next bytes on the wire will be
|
||||||
|
- // the body-following-the-timer or the subsequent request.
|
||||||
|
- // See Issue 11549.
|
||||||
|
+ // request body), don't reuse this connection.
|
||||||
|
+ //
|
||||||
|
+ // This behavior was first added on the theory that we don't know
|
||||||
|
+ // if the next bytes on the wire are going to be the remainder of
|
||||||
|
+ // the request body or the subsequent request (see issue 11549),
|
||||||
|
+ // but that's not correct: If we keep using the connection,
|
||||||
|
+ // the client is required to send the request body whether we
|
||||||
|
+ // asked for it or not.
|
||||||
|
+ //
|
||||||
|
+ // We probably do want to skip reusing the connection in most cases,
|
||||||
|
+ // however. If the client is offering a large request body that we
|
||||||
|
+ // don't intend to use, then it's better to close the connection
|
||||||
|
+ // than to read the body. For now, assume that if we're sending
|
||||||
|
+ // headers, the handler is done reading the body and we should
|
||||||
|
+ // drop the connection if we haven't seen EOF.
|
||||||
|
if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.Load() {
|
||||||
|
w.closeAfterReply = true
|
||||||
|
}
|
||||||
|
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
|
||||||
|
index c07352b018..30bce98736 100644
|
||||||
|
--- a/src/net/http/transport.go
|
||||||
|
+++ b/src/net/http/transport.go
|
||||||
|
@@ -2313,17 +2313,12 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resCode := resp.StatusCode
|
||||||
|
- if continueCh != nil {
|
||||||
|
- if resCode == 100 {
|
||||||
|
- if trace != nil && trace.Got100Continue != nil {
|
||||||
|
- trace.Got100Continue()
|
||||||
|
- }
|
||||||
|
- continueCh <- struct{}{}
|
||||||
|
- continueCh = nil
|
||||||
|
- } else if resCode >= 200 {
|
||||||
|
- close(continueCh)
|
||||||
|
- continueCh = nil
|
||||||
|
+ if continueCh != nil && resCode == StatusContinue {
|
||||||
|
+ if trace != nil && trace.Got100Continue != nil {
|
||||||
|
+ trace.Got100Continue()
|
||||||
|
}
|
||||||
|
+ continueCh <- struct{}{}
|
||||||
|
+ continueCh = nil
|
||||||
|
}
|
||||||
|
is1xx := 100 <= resCode && resCode <= 199
|
||||||
|
// treat 101 as a terminal status, see issue 26161
|
||||||
|
@@ -2346,6 +2341,25 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr
|
||||||
|
if resp.isProtocolSwitch() {
|
||||||
|
resp.Body = newReadWriteCloserBody(pc.br, pc.conn)
|
||||||
|
}
|
||||||
|
+ if continueCh != nil {
|
||||||
|
+ // We send an "Expect: 100-continue" header, but the server
|
||||||
|
+ // responded with a terminal status and no 100 Continue.
|
||||||
|
+ //
|
||||||
|
+ // If we're going to keep using the connection, we need to send the request body.
|
||||||
|
+ // Tell writeLoop to skip sending the body if we're going to close the connection,
|
||||||
|
+ // or to send it otherwise.
|
||||||
|
+ //
|
||||||
|
+ // The case where we receive a 101 Switching Protocols response is a bit
|
||||||
|
+ // ambiguous, since we don't know what protocol we're switching to.
|
||||||
|
+ // Conceivably, it's one that doesn't need us to send the body.
|
||||||
|
+ // Given that we'll send the body if ExpectContinueTimeout expires,
|
||||||
|
+ // be consistent and always send it if we aren't closing the connection.
|
||||||
|
+ if resp.Close || rc.req.Close {
|
||||||
|
+ close(continueCh) // don't send the body; the connection will close
|
||||||
|
+ } else {
|
||||||
|
+ continueCh <- struct{}{} // send the body
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
resp.TLS = pc.tlsState
|
||||||
|
return
|
||||||
|
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
|
||||||
|
index 028fecc961..b8c930ab8f 100644
|
||||||
|
--- a/src/net/http/transport_test.go
|
||||||
|
+++ b/src/net/http/transport_test.go
|
||||||
|
@@ -1135,94 +1135,142 @@ func testTransportGzip(t *testing.T, mode testMode) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-// If a request has Expect:100-continue header, the request blocks sending body until the first response.
|
||||||
|
-// Premature consumption of the request body should not be occurred.
|
||||||
|
-func TestTransportExpect100Continue(t *testing.T) {
|
||||||
|
- run(t, testTransportExpect100Continue, []testMode{http1Mode})
|
||||||
|
+// A transport100Continue test exercises Transport behaviors when sending a
|
||||||
|
+// request with an Expect: 100-continue header.
|
||||||
|
+type transport100ContinueTest struct {
|
||||||
|
+ t *testing.T
|
||||||
|
+
|
||||||
|
+ reqdone chan struct{}
|
||||||
|
+ resp *Response
|
||||||
|
+ respErr error
|
||||||
|
+
|
||||||
|
+ conn net.Conn
|
||||||
|
+ reader *bufio.Reader
|
||||||
|
}
|
||||||
|
-func testTransportExpect100Continue(t *testing.T, mode testMode) {
|
||||||
|
- ts := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
|
||||||
|
- switch req.URL.Path {
|
||||||
|
- case "/100":
|
||||||
|
- // This endpoint implicitly responds 100 Continue and reads body.
|
||||||
|
- if _, err := io.Copy(io.Discard, req.Body); err != nil {
|
||||||
|
- t.Error("Failed to read Body", err)
|
||||||
|
- }
|
||||||
|
- rw.WriteHeader(StatusOK)
|
||||||
|
- case "/200":
|
||||||
|
- // Go 1.5 adds Connection: close header if the client expect
|
||||||
|
- // continue but not entire request body is consumed.
|
||||||
|
- rw.WriteHeader(StatusOK)
|
||||||
|
- case "/500":
|
||||||
|
- rw.WriteHeader(StatusInternalServerError)
|
||||||
|
- case "/keepalive":
|
||||||
|
- // This hijacked endpoint responds error without Connection:close.
|
||||||
|
- _, bufrw, err := rw.(Hijacker).Hijack()
|
||||||
|
- if err != nil {
|
||||||
|
- log.Fatal(err)
|
||||||
|
- }
|
||||||
|
- bufrw.WriteString("HTTP/1.1 500 Internal Server Error\r\n")
|
||||||
|
- bufrw.WriteString("Content-Length: 0\r\n\r\n")
|
||||||
|
- bufrw.Flush()
|
||||||
|
- case "/timeout":
|
||||||
|
- // This endpoint tries to read body without 100 (Continue) response.
|
||||||
|
- // After ExpectContinueTimeout, the reading will be started.
|
||||||
|
- conn, bufrw, err := rw.(Hijacker).Hijack()
|
||||||
|
- if err != nil {
|
||||||
|
- log.Fatal(err)
|
||||||
|
- }
|
||||||
|
- if _, err := io.CopyN(io.Discard, bufrw, req.ContentLength); err != nil {
|
||||||
|
- t.Error("Failed to read Body", err)
|
||||||
|
- }
|
||||||
|
- bufrw.WriteString("HTTP/1.1 200 OK\r\n\r\n")
|
||||||
|
- bufrw.Flush()
|
||||||
|
- conn.Close()
|
||||||
|
- }
|
||||||
|
|
||||||
|
- })).ts
|
||||||
|
+const transport100ContinueTestBody = "request body"
|
||||||
|
|
||||||
|
- tests := []struct {
|
||||||
|
- path string
|
||||||
|
- body []byte
|
||||||
|
- sent int
|
||||||
|
- status int
|
||||||
|
- }{
|
||||||
|
- {path: "/100", body: []byte("hello"), sent: 5, status: 200}, // Got 100 followed by 200, entire body is sent.
|
||||||
|
- {path: "/200", body: []byte("hello"), sent: 0, status: 200}, // Got 200 without 100. body isn't sent.
|
||||||
|
- {path: "/500", body: []byte("hello"), sent: 0, status: 500}, // Got 500 without 100. body isn't sent.
|
||||||
|
- {path: "/keepalive", body: []byte("hello"), sent: 0, status: 500}, // Although without Connection:close, body isn't sent.
|
||||||
|
- {path: "/timeout", body: []byte("hello"), sent: 5, status: 200}, // Timeout exceeded and entire body is sent.
|
||||||
|
+// newTransport100ContinueTest creates a Transport and sends an Expect: 100-continue
|
||||||
|
+// request on it.
|
||||||
|
+func newTransport100ContinueTest(t *testing.T, timeout time.Duration) *transport100ContinueTest {
|
||||||
|
+ ln := newLocalListener(t)
|
||||||
|
+ defer ln.Close()
|
||||||
|
+
|
||||||
|
+ test := &transport100ContinueTest{
|
||||||
|
+ t: t,
|
||||||
|
+ reqdone: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
- c := ts.Client()
|
||||||
|
- for i, v := range tests {
|
||||||
|
- tr := &Transport{
|
||||||
|
- ExpectContinueTimeout: 2 * time.Second,
|
||||||
|
- }
|
||||||
|
- defer tr.CloseIdleConnections()
|
||||||
|
- c.Transport = tr
|
||||||
|
- body := bytes.NewReader(v.body)
|
||||||
|
- req, err := NewRequest("PUT", ts.URL+v.path, body)
|
||||||
|
- if err != nil {
|
||||||
|
- t.Fatal(err)
|
||||||
|
- }
|
||||||
|
+ tr := &Transport{
|
||||||
|
+ ExpectContinueTimeout: timeout,
|
||||||
|
+ }
|
||||||
|
+ go func() {
|
||||||
|
+ defer close(test.reqdone)
|
||||||
|
+ body := strings.NewReader(transport100ContinueTestBody)
|
||||||
|
+ req, _ := NewRequest("PUT", "http://"+ln.Addr().String(), body)
|
||||||
|
req.Header.Set("Expect", "100-continue")
|
||||||
|
- req.ContentLength = int64(len(v.body))
|
||||||
|
+ req.ContentLength = int64(len(transport100ContinueTestBody))
|
||||||
|
+ test.resp, test.respErr = tr.RoundTrip(req)
|
||||||
|
+ test.resp.Body.Close()
|
||||||
|
+ }()
|
||||||
|
|
||||||
|
- resp, err := c.Do(req)
|
||||||
|
- if err != nil {
|
||||||
|
- t.Fatal(err)
|
||||||
|
+ c, err := ln.Accept()
|
||||||
|
+ if err != nil {
|
||||||
|
+ t.Fatalf("Accept: %v", err)
|
||||||
|
+ }
|
||||||
|
+ t.Cleanup(func() {
|
||||||
|
+ c.Close()
|
||||||
|
+ })
|
||||||
|
+ br := bufio.NewReader(c)
|
||||||
|
+ _, err = ReadRequest(br)
|
||||||
|
+ if err != nil {
|
||||||
|
+ t.Fatalf("ReadRequest: %v", err)
|
||||||
|
+ }
|
||||||
|
+ test.conn = c
|
||||||
|
+ test.reader = br
|
||||||
|
+ t.Cleanup(func() {
|
||||||
|
+ <-test.reqdone
|
||||||
|
+ tr.CloseIdleConnections()
|
||||||
|
+ got, _ := io.ReadAll(test.reader)
|
||||||
|
+ if len(got) > 0 {
|
||||||
|
+ t.Fatalf("Transport sent unexpected bytes: %q", got)
|
||||||
|
}
|
||||||
|
- resp.Body.Close()
|
||||||
|
+ })
|
||||||
|
|
||||||
|
- sent := len(v.body) - body.Len()
|
||||||
|
- if v.status != resp.StatusCode {
|
||||||
|
- t.Errorf("test %d: status code should be %d but got %d. (%s)", i, v.status, resp.StatusCode, v.path)
|
||||||
|
- }
|
||||||
|
- if v.sent != sent {
|
||||||
|
- t.Errorf("test %d: sent body should be %d but sent %d. (%s)", i, v.sent, sent, v.path)
|
||||||
|
+ return test
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// respond sends response lines from the server to the transport.
|
||||||
|
+func (test *transport100ContinueTest) respond(lines ...string) {
|
||||||
|
+ for _, line := range lines {
|
||||||
|
+ if _, err := test.conn.Write([]byte(line + "\r\n")); err != nil {
|
||||||
|
+ test.t.Fatalf("Write: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ if _, err := test.conn.Write([]byte("\r\n")); err != nil {
|
||||||
|
+ test.t.Fatalf("Write: %v", err)
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// wantBodySent ensures the transport has sent the request body to the server.
|
||||||
|
+func (test *transport100ContinueTest) wantBodySent() {
|
||||||
|
+ got, err := io.ReadAll(io.LimitReader(test.reader, int64(len(transport100ContinueTestBody))))
|
||||||
|
+ if err != nil {
|
||||||
|
+ test.t.Fatalf("unexpected error reading body: %v", err)
|
||||||
|
+ }
|
||||||
|
+ if got, want := string(got), transport100ContinueTestBody; got != want {
|
||||||
|
+ test.t.Fatalf("unexpected body: got %q, want %q", got, want)
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// wantRequestDone ensures the Transport.RoundTrip has completed with the expected status.
|
||||||
|
+func (test *transport100ContinueTest) wantRequestDone(want int) {
|
||||||
|
+ <-test.reqdone
|
||||||
|
+ if test.respErr != nil {
|
||||||
|
+ test.t.Fatalf("unexpected RoundTrip error: %v", test.respErr)
|
||||||
|
+ }
|
||||||
|
+ if got := test.resp.StatusCode; got != want {
|
||||||
|
+ test.t.Fatalf("unexpected response code: got %v, want %v", got, want)
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+func TestTransportExpect100ContinueSent(t *testing.T) {
|
||||||
|
+ test := newTransport100ContinueTest(t, 1*time.Hour)
|
||||||
|
+ // Server sends a 100 Continue response, and the client sends the request body.
|
||||||
|
+ test.respond("HTTP/1.1 100 Continue")
|
||||||
|
+ test.wantBodySent()
|
||||||
|
+ test.respond("HTTP/1.1 200", "Content-Length: 0")
|
||||||
|
+ test.wantRequestDone(200)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+func TestTransportExpect100Continue200ResponseNoConnClose(t *testing.T) {
|
||||||
|
+ test := newTransport100ContinueTest(t, 1*time.Hour)
|
||||||
|
+ // No 100 Continue response, no Connection: close header.
|
||||||
|
+ test.respond("HTTP/1.1 200", "Content-Length: 0")
|
||||||
|
+ test.wantBodySent()
|
||||||
|
+ test.wantRequestDone(200)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+func TestTransportExpect100Continue200ResponseWithConnClose(t *testing.T) {
|
||||||
|
+ test := newTransport100ContinueTest(t, 1*time.Hour)
|
||||||
|
+ // No 100 Continue response, Connection: close header set.
|
||||||
|
+ test.respond("HTTP/1.1 200", "Connection: close", "Content-Length: 0")
|
||||||
|
+ test.wantRequestDone(200)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+func TestTransportExpect100Continue500ResponseNoConnClose(t *testing.T) {
|
||||||
|
+ test := newTransport100ContinueTest(t, 1*time.Hour)
|
||||||
|
+ // No 100 Continue response, no Connection: close header.
|
||||||
|
+ test.respond("HTTP/1.1 500", "Content-Length: 0")
|
||||||
|
+ test.wantBodySent()
|
||||||
|
+ test.wantRequestDone(500)
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+func TestTransportExpect100Continue500ResponseTimeout(t *testing.T) {
|
||||||
|
+ test := newTransport100ContinueTest(t, 5*time.Millisecond) // short timeout
|
||||||
|
+ test.wantBodySent() // after timeout
|
||||||
|
+ test.respond("HTTP/1.1 200", "Content-Length: 0")
|
||||||
|
+ test.wantRequestDone(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSOCKS5Proxy(t *testing.T) {
|
||||||
|
--
|
||||||
|
2.41.0
|
||||||
|
|
||||||
@ -66,7 +66,7 @@
|
|||||||
|
|
||||||
Name: golang
|
Name: golang
|
||||||
Version: 1.21.4
|
Version: 1.21.4
|
||||||
Release: 13
|
Release: 14
|
||||||
Summary: The Go Programming Language
|
Summary: The Go Programming Language
|
||||||
License: BSD and Public Domain
|
License: BSD and Public Domain
|
||||||
URL: https://golang.org/
|
URL: https://golang.org/
|
||||||
@ -132,6 +132,7 @@ Patch6008: backport-0008-release-branch.go1.21-net-netip-check-if-address-is-v6.
|
|||||||
Patch6009: backport-0009-Backport-cmd-go-internal-vcs-error-out-if-the-reques.patch
|
Patch6009: backport-0009-Backport-cmd-go-internal-vcs-error-out-if-the-reques.patch
|
||||||
Patch6010: backport-0010-release-branch.go1.21-net-http-limit-chunked-data-ov.patch
|
Patch6010: backport-0010-release-branch.go1.21-net-http-limit-chunked-data-ov.patch
|
||||||
Patch6011: backport-0011-Backport-archive-zip-treat-truncated-EOCDR-comment-a.patch
|
Patch6011: backport-0011-Backport-archive-zip-treat-truncated-EOCDR-comment-a.patch
|
||||||
|
Patch6012: backport-0012-net-http-send-body-or-close-connection-on-expect-100.patch
|
||||||
|
|
||||||
ExclusiveArch: %{golang_arches}
|
ExclusiveArch: %{golang_arches}
|
||||||
|
|
||||||
@ -370,6 +371,9 @@ fi
|
|||||||
%files devel -f go-tests.list -f go-misc.list -f go-src.list
|
%files devel -f go-tests.list -f go-misc.list -f go-src.list
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Wed Jul 03 2024 kywqs <weiqingsong@kylinos.cn.com> - 1.21.4-14
|
||||||
|
- fix CVE-2024-24791
|
||||||
|
|
||||||
* Sun Jun 23 2024 hanchao <hanchao63@huawei.com> - 1.21.4-13
|
* Sun Jun 23 2024 hanchao <hanchao63@huawei.com> - 1.21.4-13
|
||||||
- Type:CVE
|
- Type:CVE
|
||||||
- CVE:CVE-2023-39326,CVE-2024-24789
|
- CVE:CVE-2023-39326,CVE-2024-24789
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user