diff options
Diffstat (limited to 'meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch')
-rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch new file mode 100644 index 0000000000..58c0a484ee --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch | |||
@@ -0,0 +1,349 @@ | |||
1 | From 7917b5f31204528ea72e0629f0b7d52b35b27538 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Mon, 20 Mar 2023 10:43:19 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit parsed mime message sizes | ||
5 | |||
6 | The parsed forms of MIME headers and multipart forms can consume | ||
7 | substantially more memory than the size of the input data. | ||
8 | A malicious input containing a very large number of headers or | ||
9 | form parts can cause excessively large memory allocations. | ||
10 | |||
11 | Set limits on the size of MIME data: | ||
12 | |||
13 | Reader.NextPart and Reader.NextRawPart limit the the number | ||
14 | of headers in a part to 10000. | ||
15 | |||
16 | Reader.ReadForm limits the total number of headers in all | ||
17 | FileHeaders to 10000. | ||
18 | |||
19 | Both of these limits may be set with with | ||
20 | GODEBUG=multipartmaxheaders=<values>. | ||
21 | |||
22 | Reader.ReadForm limits the number of parts in a form to 1000. | ||
23 | This limit may be set with GODEBUG=multipartmaxparts=<value>. | ||
24 | |||
25 | Thanks for Jakob Ackermann (@das7pad) for reporting this issue. | ||
26 | |||
27 | For CVE-2023-24536 | ||
28 | For #59153 | ||
29 | For #59269 | ||
30 | |||
31 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802455 | ||
32 | Run-TryBot: Damien Neil <dneil@google.com> | ||
33 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
34 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
35 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1801087 | ||
36 | Reviewed-by: Damien Neil <dneil@google.com> | ||
37 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
38 | Change-Id: If134890d75f0d95c681d67234daf191ba08e6424 | ||
39 | Reviewed-on: https://go-review.googlesource.com/c/go/+/481985 | ||
40 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
41 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
42 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
43 | Reviewed-by: Matthew Dempsky <mdempsky@google.com> | ||
44 | |||
45 | Upstream-Status: Backport [https://github.com/golang/go/commit/7917b5f31204528ea72e0629f0b7d52b35b27538] | ||
46 | CVE: CVE-2023-24536 | ||
47 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
48 | --- | ||
49 | src/mime/multipart/formdata.go | 19 ++++++++- | ||
50 | src/mime/multipart/formdata_test.go | 61 ++++++++++++++++++++++++++++ | ||
51 | src/mime/multipart/multipart.go | 31 ++++++++++---- | ||
52 | src/mime/multipart/readmimeheader.go | 2 +- | ||
53 | src/net/textproto/reader.go | 19 +++++---- | ||
54 | 5 files changed, 115 insertions(+), 17 deletions(-) | ||
55 | |||
56 | diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go | ||
57 | index 216cccb..0b508ae 100644 | ||
58 | --- a/src/mime/multipart/formdata.go | ||
59 | +++ b/src/mime/multipart/formdata.go | ||
60 | @@ -13,6 +13,7 @@ import ( | ||
61 | "math" | ||
62 | "net/textproto" | ||
63 | "os" | ||
64 | + "strconv" | ||
65 | ) | ||
66 | |||
67 | // ErrMessageTooLarge is returned by ReadForm if the message form | ||
68 | @@ -42,6 +43,15 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
69 | numDiskFiles := 0 | ||
70 | multipartFiles := godebug.Get("multipartfiles") | ||
71 | combineFiles := multipartFiles != "distinct" | ||
72 | + maxParts := 1000 | ||
73 | + multipartMaxParts := godebug.Get("multipartmaxparts") | ||
74 | + if multipartMaxParts != "" { | ||
75 | + if v, err := strconv.Atoi(multipartMaxParts); err == nil && v >= 0 { | ||
76 | + maxParts = v | ||
77 | + } | ||
78 | + } | ||
79 | + maxHeaders := maxMIMEHeaders() | ||
80 | + | ||
81 | defer func() { | ||
82 | if file != nil { | ||
83 | if cerr := file.Close(); err == nil { | ||
84 | @@ -87,13 +97,17 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
85 | } | ||
86 | var copyBuf []byte | ||
87 | for { | ||
88 | - p, err := r.nextPart(false, maxMemoryBytes) | ||
89 | + p, err := r.nextPart(false, maxMemoryBytes, maxHeaders) | ||
90 | if err == io.EOF { | ||
91 | break | ||
92 | } | ||
93 | if err != nil { | ||
94 | return nil, err | ||
95 | } | ||
96 | + if maxParts <= 0 { | ||
97 | + return nil, ErrMessageTooLarge | ||
98 | + } | ||
99 | + maxParts-- | ||
100 | |||
101 | name := p.FormName() | ||
102 | if name == "" { | ||
103 | @@ -137,6 +151,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
104 | if maxMemoryBytes < 0 { | ||
105 | return nil, ErrMessageTooLarge | ||
106 | } | ||
107 | + for _, v := range p.Header { | ||
108 | + maxHeaders -= int64(len(v)) | ||
109 | + } | ||
110 | fh := &FileHeader{ | ||
111 | Filename: filename, | ||
112 | Header: p.Header, | ||
113 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
114 | index 8ed26e0..c78eeb7 100644 | ||
115 | --- a/src/mime/multipart/formdata_test.go | ||
116 | +++ b/src/mime/multipart/formdata_test.go | ||
117 | @@ -360,6 +360,67 @@ func testReadFormManyFiles(t *testing.T, distinct bool) { | ||
118 | } | ||
119 | } | ||
120 | |||
121 | +func TestReadFormLimits(t *testing.T) { | ||
122 | + for _, test := range []struct { | ||
123 | + values int | ||
124 | + files int | ||
125 | + extraKeysPerFile int | ||
126 | + wantErr error | ||
127 | + godebug string | ||
128 | + }{ | ||
129 | + {values: 1000}, | ||
130 | + {values: 1001, wantErr: ErrMessageTooLarge}, | ||
131 | + {values: 500, files: 500}, | ||
132 | + {values: 501, files: 500, wantErr: ErrMessageTooLarge}, | ||
133 | + {files: 1000}, | ||
134 | + {files: 1001, wantErr: ErrMessageTooLarge}, | ||
135 | + {files: 1, extraKeysPerFile: 9998}, // plus Content-Disposition and Content-Type | ||
136 | + {files: 1, extraKeysPerFile: 10000, wantErr: ErrMessageTooLarge}, | ||
137 | + {godebug: "multipartmaxparts=100", values: 100}, | ||
138 | + {godebug: "multipartmaxparts=100", values: 101, wantErr: ErrMessageTooLarge}, | ||
139 | + {godebug: "multipartmaxheaders=100", files: 2, extraKeysPerFile: 48}, | ||
140 | + {godebug: "multipartmaxheaders=100", files: 2, extraKeysPerFile: 50, wantErr: ErrMessageTooLarge}, | ||
141 | + } { | ||
142 | + name := fmt.Sprintf("values=%v/files=%v/extraKeysPerFile=%v", test.values, test.files, test.extraKeysPerFile) | ||
143 | + if test.godebug != "" { | ||
144 | + name += fmt.Sprintf("/godebug=%v", test.godebug) | ||
145 | + } | ||
146 | + t.Run(name, func(t *testing.T) { | ||
147 | + if test.godebug != "" { | ||
148 | + t.Setenv("GODEBUG", test.godebug) | ||
149 | + } | ||
150 | + var buf bytes.Buffer | ||
151 | + fw := NewWriter(&buf) | ||
152 | + for i := 0; i < test.values; i++ { | ||
153 | + w, _ := fw.CreateFormField(fmt.Sprintf("field%v", i)) | ||
154 | + fmt.Fprintf(w, "value %v", i) | ||
155 | + } | ||
156 | + for i := 0; i < test.files; i++ { | ||
157 | + h := make(textproto.MIMEHeader) | ||
158 | + h.Set("Content-Disposition", | ||
159 | + fmt.Sprintf(`form-data; name="file%v"; filename="file%v"`, i, i)) | ||
160 | + h.Set("Content-Type", "application/octet-stream") | ||
161 | + for j := 0; j < test.extraKeysPerFile; j++ { | ||
162 | + h.Set(fmt.Sprintf("k%v", j), "v") | ||
163 | + } | ||
164 | + w, _ := fw.CreatePart(h) | ||
165 | + fmt.Fprintf(w, "value %v", i) | ||
166 | + } | ||
167 | + if err := fw.Close(); err != nil { | ||
168 | + t.Fatal(err) | ||
169 | + } | ||
170 | + fr := NewReader(bytes.NewReader(buf.Bytes()), fw.Boundary()) | ||
171 | + form, err := fr.ReadForm(1 << 10) | ||
172 | + if err == nil { | ||
173 | + defer form.RemoveAll() | ||
174 | + } | ||
175 | + if err != test.wantErr { | ||
176 | + t.Errorf("ReadForm = %v, want %v", err, test.wantErr) | ||
177 | + } | ||
178 | + }) | ||
179 | + } | ||
180 | +} | ||
181 | + | ||
182 | func BenchmarkReadForm(b *testing.B) { | ||
183 | for _, test := range []struct { | ||
184 | name string | ||
185 | diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go | ||
186 | index 958cef8..94464a8 100644 | ||
187 | --- a/src/mime/multipart/multipart.go | ||
188 | +++ b/src/mime/multipart/multipart.go | ||
189 | @@ -16,11 +16,13 @@ import ( | ||
190 | "bufio" | ||
191 | "bytes" | ||
192 | "fmt" | ||
193 | + "internal/godebug" | ||
194 | "io" | ||
195 | "io/ioutil" | ||
196 | "mime" | ||
197 | "mime/quotedprintable" | ||
198 | "net/textproto" | ||
199 | + "strconv" | ||
200 | "strings" | ||
201 | ) | ||
202 | |||
203 | @@ -121,12 +123,12 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) { | ||
204 | return n, r.err | ||
205 | } | ||
206 | |||
207 | -func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { | ||
208 | +func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) { | ||
209 | bp := &Part{ | ||
210 | Header: make(map[string][]string), | ||
211 | mr: mr, | ||
212 | } | ||
213 | - if err := bp.populateHeaders(maxMIMEHeaderSize); err != nil { | ||
214 | + if err := bp.populateHeaders(maxMIMEHeaderSize, maxMIMEHeaders); err != nil { | ||
215 | return nil, err | ||
216 | } | ||
217 | bp.r = partReader{bp} | ||
218 | @@ -142,9 +144,9 @@ func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { | ||
219 | return bp, nil | ||
220 | } | ||
221 | |||
222 | -func (bp *Part) populateHeaders(maxMIMEHeaderSize int64) error { | ||
223 | +func (bp *Part) populateHeaders(maxMIMEHeaderSize, maxMIMEHeaders int64) error { | ||
224 | r := textproto.NewReader(bp.mr.bufReader) | ||
225 | - header, err := readMIMEHeader(r, maxMIMEHeaderSize) | ||
226 | + header, err := readMIMEHeader(r, maxMIMEHeaderSize, maxMIMEHeaders) | ||
227 | if err == nil { | ||
228 | bp.Header = header | ||
229 | } | ||
230 | @@ -306,6 +308,19 @@ type Reader struct { | ||
231 | // including header keys, values, and map overhead. | ||
232 | const maxMIMEHeaderSize = 10 << 20 | ||
233 | |||
234 | +func maxMIMEHeaders() int64 { | ||
235 | + // multipartMaxHeaders is the maximum number of header entries NextPart will return, | ||
236 | + // as well as the maximum combined total of header entries Reader.ReadForm will return | ||
237 | + // in FileHeaders. | ||
238 | + multipartMaxHeaders := godebug.Get("multipartmaxheaders") | ||
239 | + if multipartMaxHeaders != "" { | ||
240 | + if v, err := strconv.ParseInt(multipartMaxHeaders, 10, 64); err == nil && v >= 0 { | ||
241 | + return v | ||
242 | + } | ||
243 | + } | ||
244 | + return 10000 | ||
245 | +} | ||
246 | + | ||
247 | // NextPart returns the next part in the multipart or an error. | ||
248 | // When there are no more parts, the error io.EOF is returned. | ||
249 | // | ||
250 | @@ -313,7 +328,7 @@ const maxMIMEHeaderSize = 10 << 20 | ||
251 | // has a value of "quoted-printable", that header is instead | ||
252 | // hidden and the body is transparently decoded during Read calls. | ||
253 | func (r *Reader) NextPart() (*Part, error) { | ||
254 | - return r.nextPart(false, maxMIMEHeaderSize) | ||
255 | + return r.nextPart(false, maxMIMEHeaderSize, maxMIMEHeaders()) | ||
256 | } | ||
257 | |||
258 | // NextRawPart returns the next part in the multipart or an error. | ||
259 | @@ -322,10 +337,10 @@ func (r *Reader) NextPart() (*Part, error) { | ||
260 | // Unlike NextPart, it does not have special handling for | ||
261 | // "Content-Transfer-Encoding: quoted-printable". | ||
262 | func (r *Reader) NextRawPart() (*Part, error) { | ||
263 | - return r.nextPart(true, maxMIMEHeaderSize) | ||
264 | + return r.nextPart(true, maxMIMEHeaderSize, maxMIMEHeaders()) | ||
265 | } | ||
266 | |||
267 | -func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { | ||
268 | +func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) { | ||
269 | if r.currentPart != nil { | ||
270 | r.currentPart.Close() | ||
271 | } | ||
272 | @@ -350,7 +365,7 @@ func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) | ||
273 | |||
274 | if r.isBoundaryDelimiterLine(line) { | ||
275 | r.partsRead++ | ||
276 | - bp, err := newPart(r, rawPart, maxMIMEHeaderSize) | ||
277 | + bp, err := newPart(r, rawPart, maxMIMEHeaderSize, maxMIMEHeaders) | ||
278 | if err != nil { | ||
279 | return nil, err | ||
280 | } | ||
281 | diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go | ||
282 | index 6836928..25aa6e2 100644 | ||
283 | --- a/src/mime/multipart/readmimeheader.go | ||
284 | +++ b/src/mime/multipart/readmimeheader.go | ||
285 | @@ -11,4 +11,4 @@ import ( | ||
286 | // readMIMEHeader is defined in package net/textproto. | ||
287 | // | ||
288 | //go:linkname readMIMEHeader net/textproto.readMIMEHeader | ||
289 | -func readMIMEHeader(r *textproto.Reader, lim int64) (textproto.MIMEHeader, error) | ||
290 | +func readMIMEHeader(r *textproto.Reader, maxMemory, maxHeaders int64) (textproto.MIMEHeader, error) | ||
291 | diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go | ||
292 | index 1c79f0a..ad2d777 100644 | ||
293 | --- a/src/net/textproto/reader.go | ||
294 | +++ b/src/net/textproto/reader.go | ||
295 | @@ -484,12 +484,12 @@ func (r *Reader) ReadDotLines() ([]string, error) { | ||
296 | // } | ||
297 | // | ||
298 | func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { | ||
299 | - return readMIMEHeader(r, math.MaxInt64) | ||
300 | + return readMIMEHeader(r, math.MaxInt64, math.MaxInt64) | ||
301 | } | ||
302 | |||
303 | // readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size. | ||
304 | // It is called by the mime/multipart package. | ||
305 | -func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
306 | +func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) { | ||
307 | // Avoid lots of small slice allocations later by allocating one | ||
308 | // large one ahead of time which we'll cut up into smaller | ||
309 | // slices. If this isn't big enough later, we allocate small ones. | ||
310 | @@ -507,7 +507,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
311 | // Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry. | ||
312 | // Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large | ||
313 | // MIMEHeaders average about 200 bytes per entry. | ||
314 | - lim -= 400 | ||
315 | + maxMemory -= 400 | ||
316 | const mapEntryOverhead = 200 | ||
317 | |||
318 | // The first line cannot start with a leading space. | ||
319 | @@ -539,6 +539,11 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
320 | continue | ||
321 | } | ||
322 | |||
323 | + maxHeaders-- | ||
324 | + if maxHeaders < 0 { | ||
325 | + return nil, errors.New("message too large") | ||
326 | + } | ||
327 | + | ||
328 | // backport 5c55ac9bf1e5f779220294c843526536605f42ab | ||
329 | // | ||
330 | // value is computed as | ||
331 | @@ -557,11 +562,11 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
332 | |||
333 | vv := m[key] | ||
334 | if vv == nil { | ||
335 | - lim -= int64(len(key)) | ||
336 | - lim -= mapEntryOverhead | ||
337 | + maxMemory -= int64(len(key)) | ||
338 | + maxMemory -= mapEntryOverhead | ||
339 | } | ||
340 | - lim -= int64(len(value)) | ||
341 | - if lim < 0 { | ||
342 | + maxMemory -= int64(len(value)) | ||
343 | + if maxMemory < 0 { | ||
344 | // TODO: This should be a distinguishable error (ErrMessageTooLarge) | ||
345 | // to allow mime/multipart to detect it. | ||
346 | return m, errors.New("message too large") | ||
347 | -- | ||
348 | 2.25.1 | ||
349 | |||