diff options
author | Vijay Anusuri <vanusuri@mvista.com> | 2023-12-27 07:14:26 +0530 |
---|---|---|
committer | Steve Sakoman <steve@sakoman.com> | 2024-01-05 03:25:38 -1000 |
commit | a2bf2f28c4f2ae2ab19a963d801029abb7de5dc9 (patch) | |
tree | 94aa1a6eabeafb80f75017a33ff5db19dddbad9c | |
parent | c0e5370a91c87145d0a8eb753241b04ee3b1928e (diff) | |
download | poky-a2bf2f28c4f2ae2ab19a963d801029abb7de5dc9.tar.gz |
go: Fix CVE-2023-39326
A malicious HTTP sender can use chunk extensions to cause a receiver
reading from a request or response body to read many more bytes from
the network than are in the body. A malicious HTTP client can further
exploit this to cause a server to automatically read a large amount
of data (up to about 1GiB) when a handler fails to read the entire
body of a request. Chunk extensions are a little-used HTTP feature
which permit including additional metadata in a request or response
body sent using the chunked encoding. The net/http chunked encoding
reader discards this metadata. A sender can exploit this by inserting
a large metadata segment with each byte transferred. The chunk reader
now produces an error if the ratio of real body to encoded bytes grows
too small.
References:
https://nvd.nist.gov/vuln/detail/CVE-2023-39326
https://security-tracker.debian.org/tracker/CVE-2023-39326
(From OE-Core rev: 5b55648f3142762c9563289c1b19aa3b7de27164)
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r-- | meta/recipes-devtools/go/go-1.14.inc | 1 | ||||
-rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2023-39326.patch | 181 |
2 files changed, 182 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14.inc b/meta/recipes-devtools/go/go-1.14.inc index 091b778de8..b827a3606d 100644 --- a/meta/recipes-devtools/go/go-1.14.inc +++ b/meta/recipes-devtools/go/go-1.14.inc | |||
@@ -82,6 +82,7 @@ SRC_URI += "\ | |||
82 | file://CVE-2023-24536_3.patch \ | 82 | file://CVE-2023-24536_3.patch \ |
83 | file://CVE-2023-39318.patch \ | 83 | file://CVE-2023-39318.patch \ |
84 | file://CVE-2023-39319.patch \ | 84 | file://CVE-2023-39319.patch \ |
85 | file://CVE-2023-39326.patch \ | ||
85 | " | 86 | " |
86 | 87 | ||
87 | SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" | 88 | SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" |
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-39326.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-39326.patch new file mode 100644 index 0000000000..998af361e8 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-39326.patch | |||
@@ -0,0 +1,181 @@ | |||
1 | From 6446af942e2e2b161c4ec1b60d9703a2b55dc4dd Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Tue, 7 Nov 2023 10:47:56 -0800 | ||
4 | Subject: [PATCH] [release-branch.go1.20] net/http: limit chunked data overhead | ||
5 | |||
6 | The chunked transfer encoding adds some overhead to | ||
7 | the content transferred. When writing one byte per | ||
8 | chunk, for example, there are five bytes of overhead | ||
9 | per byte of data transferred: "1\r\nX\r\n" to send "X". | ||
10 | |||
11 | Chunks may include "chunk extensions", | ||
12 | which we skip over and do not use. | ||
13 | For example: "1;chunk extension here\r\nX\r\n". | ||
14 | |||
15 | A malicious sender can use chunk extensions to add | ||
16 | about 4k of overhead per byte of data. | ||
17 | (The maximum chunk header line size we will accept.) | ||
18 | |||
19 | Track the amount of overhead read in chunked data, | ||
20 | and produce an error if it seems excessive. | ||
21 | |||
22 | Updates #64433 | ||
23 | Fixes #64434 | ||
24 | Fixes CVE-2023-39326 | ||
25 | |||
26 | Change-Id: I40f8d70eb6f9575fb43f506eb19132ccedafcf39 | ||
27 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2076135 | ||
28 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
29 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
30 | (cherry picked from commit 3473ae72ee66c60744665a24b2fde143e8964d4f) | ||
31 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2095407 | ||
32 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
33 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
34 | Reviewed-by: Damien Neil <dneil@google.com> | ||
35 | Reviewed-on: https://go-review.googlesource.com/c/go/+/547355 | ||
36 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
37 | LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> | ||
38 | |||
39 | Upstream-Status: Backport [https://github.com/golang/go/commit/6446af942e2e2b161c4ec1b60d9703a2b55dc4dd] | ||
40 | CVE: CVE-2023-39326 | ||
41 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
42 | --- | ||
43 | src/net/http/internal/chunked.go | 36 +++++++++++++--- | ||
44 | src/net/http/internal/chunked_test.go | 59 +++++++++++++++++++++++++++ | ||
45 | 2 files changed, 89 insertions(+), 6 deletions(-) | ||
46 | |||
47 | diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go | ||
48 | index f06e572..ddbaacb 100644 | ||
49 | --- a/src/net/http/internal/chunked.go | ||
50 | +++ b/src/net/http/internal/chunked.go | ||
51 | @@ -39,7 +39,8 @@ type chunkedReader struct { | ||
52 | n uint64 // unread bytes in chunk | ||
53 | err error | ||
54 | buf [2]byte | ||
55 | - checkEnd bool // whether need to check for \r\n chunk footer | ||
56 | + checkEnd bool // whether need to check for \r\n chunk footer | ||
57 | + excess int64 // "excessive" chunk overhead, for malicious sender detection | ||
58 | } | ||
59 | |||
60 | func (cr *chunkedReader) beginChunk() { | ||
61 | @@ -49,10 +50,38 @@ func (cr *chunkedReader) beginChunk() { | ||
62 | if cr.err != nil { | ||
63 | return | ||
64 | } | ||
65 | + cr.excess += int64(len(line)) + 2 // header, plus \r\n after the chunk data | ||
66 | + line = trimTrailingWhitespace(line) | ||
67 | + line, cr.err = removeChunkExtension(line) | ||
68 | + if cr.err != nil { | ||
69 | + return | ||
70 | + } | ||
71 | cr.n, cr.err = parseHexUint(line) | ||
72 | if cr.err != nil { | ||
73 | return | ||
74 | } | ||
75 | + // A sender who sends one byte per chunk will send 5 bytes of overhead | ||
76 | + // for every byte of data. ("1\r\nX\r\n" to send "X".) | ||
77 | + // We want to allow this, since streaming a byte at a time can be legitimate. | ||
78 | + // | ||
79 | + // A sender can use chunk extensions to add arbitrary amounts of additional | ||
80 | + // data per byte read. ("1;very long extension\r\nX\r\n" to send "X".) | ||
81 | + // We don't want to disallow extensions (although we discard them), | ||
82 | + // but we also don't want to allow a sender to reduce the signal/noise ratio | ||
83 | + // arbitrarily. | ||
84 | + // | ||
85 | + // We track the amount of excess overhead read, | ||
86 | + // and produce an error if it grows too large. | ||
87 | + // | ||
88 | + // Currently, we say that we're willing to accept 16 bytes of overhead per chunk, | ||
89 | + // plus twice the amount of real data in the chunk. | ||
90 | + cr.excess -= 16 + (2 * int64(cr.n)) | ||
91 | + if cr.excess < 0 { | ||
92 | + cr.excess = 0 | ||
93 | + } | ||
94 | + if cr.excess > 16*1024 { | ||
95 | + cr.err = errors.New("chunked encoding contains too much non-data") | ||
96 | + } | ||
97 | if cr.n == 0 { | ||
98 | cr.err = io.EOF | ||
99 | } | ||
100 | @@ -133,11 +162,6 @@ func readChunkLine(b *bufio.Reader) ([]byte, error) { | ||
101 | if len(p) >= maxLineLength { | ||
102 | return nil, ErrLineTooLong | ||
103 | } | ||
104 | - p = trimTrailingWhitespace(p) | ||
105 | - p, err = removeChunkExtension(p) | ||
106 | - if err != nil { | ||
107 | - return nil, err | ||
108 | - } | ||
109 | return p, nil | ||
110 | } | ||
111 | |||
112 | diff --git a/src/net/http/internal/chunked_test.go b/src/net/http/internal/chunked_test.go | ||
113 | index d067165..b20747d 100644 | ||
114 | --- a/src/net/http/internal/chunked_test.go | ||
115 | +++ b/src/net/http/internal/chunked_test.go | ||
116 | @@ -212,3 +212,62 @@ func TestChunkReadPartial(t *testing.T) { | ||
117 | } | ||
118 | |||
119 | } | ||
120 | + | ||
121 | +func TestChunkReaderTooMuchOverhead(t *testing.T) { | ||
122 | + // If the sender is sending 100x as many chunk header bytes as chunk data, | ||
123 | + // we should reject the stream at some point. | ||
124 | + chunk := []byte("1;") | ||
125 | + for i := 0; i < 100; i++ { | ||
126 | + chunk = append(chunk, 'a') // chunk extension | ||
127 | + } | ||
128 | + chunk = append(chunk, "\r\nX\r\n"...) | ||
129 | + const bodylen = 1 << 20 | ||
130 | + r := NewChunkedReader(&funcReader{f: func(i int) ([]byte, error) { | ||
131 | + if i < bodylen { | ||
132 | + return chunk, nil | ||
133 | + } | ||
134 | + return []byte("0\r\n"), nil | ||
135 | + }}) | ||
136 | + _, err := io.ReadAll(r) | ||
137 | + if err == nil { | ||
138 | + t.Fatalf("successfully read body with excessive overhead; want error") | ||
139 | + } | ||
140 | +} | ||
141 | + | ||
142 | +func TestChunkReaderByteAtATime(t *testing.T) { | ||
143 | + // Sending one byte per chunk should not trip the excess-overhead detection. | ||
144 | + const bodylen = 1 << 20 | ||
145 | + r := NewChunkedReader(&funcReader{f: func(i int) ([]byte, error) { | ||
146 | + if i < bodylen { | ||
147 | + return []byte("1\r\nX\r\n"), nil | ||
148 | + } | ||
149 | + return []byte("0\r\n"), nil | ||
150 | + }}) | ||
151 | + got, err := io.ReadAll(r) | ||
152 | + if err != nil { | ||
153 | + t.Errorf("unexpected error: %v", err) | ||
154 | + } | ||
155 | + if len(got) != bodylen { | ||
156 | + t.Errorf("read %v bytes, want %v", len(got), bodylen) | ||
157 | + } | ||
158 | +} | ||
159 | + | ||
160 | +type funcReader struct { | ||
161 | + f func(iteration int) ([]byte, error) | ||
162 | + i int | ||
163 | + b []byte | ||
164 | + err error | ||
165 | +} | ||
166 | + | ||
167 | +func (r *funcReader) Read(p []byte) (n int, err error) { | ||
168 | + if len(r.b) == 0 && r.err == nil { | ||
169 | + r.b, r.err = r.f(r.i) | ||
170 | + r.i++ | ||
171 | + } | ||
172 | + n = copy(p, r.b) | ||
173 | + r.b = r.b[n:] | ||
174 | + if len(r.b) > 0 { | ||
175 | + return n, nil | ||
176 | + } | ||
177 | + return n, r.err | ||
178 | +} | ||
179 | -- | ||
180 | 2.25.1 | ||
181 | |||