summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSoumya Sambu <soumya.sambu@windriver.com>2023-12-20 12:03:38 +0000
committerSteve Sakoman <steve@sakoman.com>2023-12-22 16:36:55 -1000
commit7262c0f2351cd08f049c7d050e8672c45e1952c0 (patch)
tree3b70c3b29d9fbd0277a21a3bdc7d767461499a2f
parent558325482c9538acf87eb0014babe0fc26f0471f (diff)
downloadpoky-7262c0f2351cd08f049c7d050e8672c45e1952c0.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: 448df3bb9277287dd8586987199223b7314fdd01) Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-devtools/go/go-1.17.13.inc1
-rw-r--r--meta/recipes-devtools/go/go-1.20/CVE-2023-39326.patch182
2 files changed, 183 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.17.13.inc b/meta/recipes-devtools/go/go-1.17.13.inc
index 330f571d22..95c4461d3e 100644
--- a/meta/recipes-devtools/go/go-1.17.13.inc
+++ b/meta/recipes-devtools/go/go-1.17.13.inc
@@ -47,6 +47,7 @@ SRC_URI += "\
47 file://CVE-2023-29409.patch \ 47 file://CVE-2023-29409.patch \
48 file://CVE-2023-39319.patch \ 48 file://CVE-2023-39319.patch \
49 file://CVE-2023-39318.patch \ 49 file://CVE-2023-39318.patch \
50 file://CVE-2023-39326.patch \
50" 51"
51SRC_URI[main.sha256sum] = "a1a48b23afb206f95e7bbaa9b898d965f90826f6f1d1fc0c1d784ada0cd300fd" 52SRC_URI[main.sha256sum] = "a1a48b23afb206f95e7bbaa9b898d965f90826f6f1d1fc0c1d784ada0cd300fd"
52 53
diff --git a/meta/recipes-devtools/go/go-1.20/CVE-2023-39326.patch b/meta/recipes-devtools/go/go-1.20/CVE-2023-39326.patch
new file mode 100644
index 0000000000..ca78e552c2
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.20/CVE-2023-39326.patch
@@ -0,0 +1,182 @@
1From 6446af942e2e2b161c4ec1b60d9703a2b55dc4dd Mon Sep 17 00:00:00 2001
2From: Damien Neil <dneil@google.com>
3Date: Tue, 7 Nov 2023 10:47:56 -0800
4Subject: [PATCH] net/http: limit chunked data overhead
5
6The chunked transfer encoding adds some overhead to
7the content transferred. When writing one byte per
8chunk, for example, there are five bytes of overhead
9per byte of data transferred: "1\r\nX\r\n" to send "X".
10
11Chunks may include "chunk extensions",
12which we skip over and do not use.
13For example: "1;chunk extension here\r\nX\r\n".
14
15A malicious sender can use chunk extensions to add
16about 4k of overhead per byte of data.
17(The maximum chunk header line size we will accept.)
18
19Track the amount of overhead read in chunked data,
20and produce an error if it seems excessive.
21
22Updates #64433
23Fixes #64434
24Fixes CVE-2023-39326
25
26Change-Id: I40f8d70eb6f9575fb43f506eb19132ccedafcf39
27Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2076135
28Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
29Reviewed-by: Roland Shoemaker <bracewell@google.com>
30(cherry picked from commit 3473ae72ee66c60744665a24b2fde143e8964d4f)
31Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2095407
32Run-TryBot: Roland Shoemaker <bracewell@google.com>
33TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com>
34Reviewed-by: Damien Neil <dneil@google.com>
35Reviewed-on: https://go-review.googlesource.com/c/go/+/547355
36Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
37LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
38
39CVE: CVE-2023-39326
40
41Upstream-Status: Backport [https://github.com/golang/go/commit/6446af942e2e2b161c4ec1b60d9703a2b55dc4dd]
42
43Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
44---
45 src/net/http/internal/chunked.go | 36 +++++++++++++---
46 src/net/http/internal/chunked_test.go | 59 +++++++++++++++++++++++++++
47 2 files changed, 89 insertions(+), 6 deletions(-)
48
49diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go
50index f06e572..ddbaacb 100644
51--- a/src/net/http/internal/chunked.go
52+++ b/src/net/http/internal/chunked.go
53@@ -39,7 +39,8 @@ type chunkedReader struct {
54 n uint64 // unread bytes in chunk
55 err error
56 buf [2]byte
57- checkEnd bool // whether need to check for \r\n chunk footer
58+ checkEnd bool // whether need to check for \r\n chunk footer
59+ excess int64 // "excessive" chunk overhead, for malicious sender detection
60 }
61
62 func (cr *chunkedReader) beginChunk() {
63@@ -49,10 +50,38 @@ func (cr *chunkedReader) beginChunk() {
64 if cr.err != nil {
65 return
66 }
67+ cr.excess += int64(len(line)) + 2 // header, plus \r\n after the chunk data
68+ line = trimTrailingWhitespace(line)
69+ line, cr.err = removeChunkExtension(line)
70+ if cr.err != nil {
71+ return
72+ }
73 cr.n, cr.err = parseHexUint(line)
74 if cr.err != nil {
75 return
76 }
77+ // A sender who sends one byte per chunk will send 5 bytes of overhead
78+ // for every byte of data. ("1\r\nX\r\n" to send "X".)
79+ // We want to allow this, since streaming a byte at a time can be legitimate.
80+ //
81+ // A sender can use chunk extensions to add arbitrary amounts of additional
82+ // data per byte read. ("1;very long extension\r\nX\r\n" to send "X".)
83+ // We don't want to disallow extensions (although we discard them),
84+ // but we also don't want to allow a sender to reduce the signal/noise ratio
85+ // arbitrarily.
86+ //
87+ // We track the amount of excess overhead read,
88+ // and produce an error if it grows too large.
89+ //
90+ // Currently, we say that we're willing to accept 16 bytes of overhead per chunk,
91+ // plus twice the amount of real data in the chunk.
92+ cr.excess -= 16 + (2 * int64(cr.n))
93+ if cr.excess < 0 {
94+ cr.excess = 0
95+ }
96+ if cr.excess > 16*1024 {
97+ cr.err = errors.New("chunked encoding contains too much non-data")
98+ }
99 if cr.n == 0 {
100 cr.err = io.EOF
101 }
102@@ -133,11 +162,6 @@ func readChunkLine(b *bufio.Reader) ([]byte, error) {
103 if len(p) >= maxLineLength {
104 return nil, ErrLineTooLong
105 }
106- p = trimTrailingWhitespace(p)
107- p, err = removeChunkExtension(p)
108- if err != nil {
109- return nil, err
110- }
111 return p, nil
112 }
113
114diff --git a/src/net/http/internal/chunked_test.go b/src/net/http/internal/chunked_test.go
115index 08152ed..5fbeb08 100644
116--- a/src/net/http/internal/chunked_test.go
117+++ b/src/net/http/internal/chunked_test.go
118@@ -211,3 +211,62 @@ func TestChunkReadPartial(t *testing.T) {
119 }
120
121 }
122+
123+func TestChunkReaderTooMuchOverhead(t *testing.T) {
124+ // If the sender is sending 100x as many chunk header bytes as chunk data,
125+ // we should reject the stream at some point.
126+ chunk := []byte("1;")
127+ for i := 0; i < 100; i++ {
128+ chunk = append(chunk, 'a') // chunk extension
129+ }
130+ chunk = append(chunk, "\r\nX\r\n"...)
131+ const bodylen = 1 << 20
132+ r := NewChunkedReader(&funcReader{f: func(i int) ([]byte, error) {
133+ if i < bodylen {
134+ return chunk, nil
135+ }
136+ return []byte("0\r\n"), nil
137+ }})
138+ _, err := io.ReadAll(r)
139+ if err == nil {
140+ t.Fatalf("successfully read body with excessive overhead; want error")
141+ }
142+}
143+
144+func TestChunkReaderByteAtATime(t *testing.T) {
145+ // Sending one byte per chunk should not trip the excess-overhead detection.
146+ const bodylen = 1 << 20
147+ r := NewChunkedReader(&funcReader{f: func(i int) ([]byte, error) {
148+ if i < bodylen {
149+ return []byte("1\r\nX\r\n"), nil
150+ }
151+ return []byte("0\r\n"), nil
152+ }})
153+ got, err := io.ReadAll(r)
154+ if err != nil {
155+ t.Errorf("unexpected error: %v", err)
156+ }
157+ if len(got) != bodylen {
158+ t.Errorf("read %v bytes, want %v", len(got), bodylen)
159+ }
160+}
161+
162+type funcReader struct {
163+ f func(iteration int) ([]byte, error)
164+ i int
165+ b []byte
166+ err error
167+}
168+
169+func (r *funcReader) Read(p []byte) (n int, err error) {
170+ if len(r.b) == 0 && r.err == nil {
171+ r.b, r.err = r.f(r.i)
172+ r.i++
173+ }
174+ n = copy(p, r.b)
175+ r.b = r.b[n:]
176+ if len(r.b) > 0 {
177+ return n, nil
178+ }
179+ return n, r.err
180+}
181--
1822.40.0