summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch')
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch660
1 files changed, 660 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch
new file mode 100644
index 0000000000..5f80c62b0b
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch
@@ -0,0 +1,660 @@
1From 5c55ac9bf1e5f779220294c843526536605f42ab Mon Sep 17 00:00:00 2001
2From: Damien Neil <dneil@google.com>
3Date: Wed, 25 Jan 2023 09:27:01 -0800
4Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit memory/inode consumption of ReadForm
5
6Reader.ReadForm is documented as storing "up to maxMemory bytes + 10MB"
7in memory. Parsed forms can consume substantially more memory than
8this limit, since ReadForm does not account for map entry overhead
9and MIME headers.
10
11In addition, while the amount of disk memory consumed by ReadForm can
12be constrained by limiting the size of the parsed input, ReadForm will
13create one temporary file per form part stored on disk, potentially
14consuming a large number of inodes.
15
16Update ReadForm's memory accounting to include part names,
17MIME headers, and map entry overhead.
18
19Update ReadForm to store all on-disk file parts in a single
20temporary file.
21
22Files returned by FileHeader.Open are documented as having a concrete
23type of *os.File when a file is stored on disk. The change to use a
24single temporary file for all parts means that this is no longer the
25case when a form contains more than a single file part stored on disk.
26
27The previous behavior of storing each file part in a separate disk
28file may be reenabled with GODEBUG=multipartfiles=distinct.
29
30Update Reader.NextPart and Reader.NextRawPart to set a 10MiB cap
31on the size of MIME headers.
32
33Thanks to Jakob Ackermann (@das7pad) for reporting this issue.
34
35Updates #58006
36Fixes #58362
37Fixes CVE-2022-41725
38
39Change-Id: Ibd780a6c4c83ac8bcfd3cbe344f042e9940f2eab
40Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1714276
41Reviewed-by: Julie Qiu <julieqiu@google.com>
42TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com>
43Reviewed-by: Roland Shoemaker <bracewell@google.com>
44Run-TryBot: Damien Neil <dneil@google.com>
45(cherry picked from commit ed4664330edcd91b24914c9371c377c132dbce8c)
46Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728949
47Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
48Run-TryBot: Roland Shoemaker <bracewell@google.com>
49Reviewed-by: Damien Neil <dneil@google.com>
50Reviewed-on: https://go-review.googlesource.com/c/go/+/468116
51TryBot-Result: Gopher Robot <gobot@golang.org>
52Reviewed-by: Than McIntosh <thanm@google.com>
53Run-TryBot: Michael Pratt <mpratt@google.com>
54Auto-Submit: Michael Pratt <mpratt@google.com>
55
56Upstream-Status: Backport [https://github.com/golang/go/commit/5c55ac9bf1e5f779220294c843526536605f42ab]
57CVE: CVE-2022-41725
58Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
59---
60 src/mime/multipart/formdata.go | 132 ++++++++++++++++++++-----
61 src/mime/multipart/formdata_test.go | 140 ++++++++++++++++++++++++++-
62 src/mime/multipart/multipart.go | 25 +++--
63 src/mime/multipart/readmimeheader.go | 14 +++
64 src/net/http/request_test.go | 2 +-
65 src/net/textproto/reader.go | 27 ++++++
66 6 files changed, 303 insertions(+), 37 deletions(-)
67 create mode 100644 src/mime/multipart/readmimeheader.go
68
69diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
70index 9c42ea8..1eeb340 100644
71--- a/src/mime/multipart/formdata.go
72+++ b/src/mime/multipart/formdata.go
73@@ -7,6 +7,7 @@ package multipart
74 import (
75 "bytes"
76 "errors"
77+ "internal/godebug"
78 "io"
79 "io/ioutil"
80 "math"
81@@ -34,23 +35,58 @@ func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {
82
83 func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
84 form := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
85+ var (
86+ file *os.File
87+ fileOff int64
88+ )
89+ numDiskFiles := 0
90+ multipartFiles := godebug.Get("multipartfiles")
91+ combineFiles := multipartFiles != "distinct"
92 defer func() {
93+ if file != nil {
94+ if cerr := file.Close(); err == nil {
95+ err = cerr
96+ }
97+ }
98+ if combineFiles && numDiskFiles > 1 {
99+ for _, fhs := range form.File {
100+ for _, fh := range fhs {
101+ fh.tmpshared = true
102+ }
103+ }
104+ }
105 if err != nil {
106 form.RemoveAll()
107+ if file != nil {
108+ os.Remove(file.Name())
109+ }
110 }
111 }()
112
113- // Reserve an additional 10 MB for non-file parts.
114- maxValueBytes := maxMemory + int64(10<<20)
115- if maxValueBytes <= 0 {
116+ // maxFileMemoryBytes is the maximum bytes of file data we will store in memory.
117+ // Data past this limit is written to disk.
118+ // This limit strictly applies to content, not metadata (filenames, MIME headers, etc.),
119+ // since metadata is always stored in memory, not disk.
120+ //
121+ // maxMemoryBytes is the maximum bytes we will store in memory, including file content,
122+ // non-file part values, metdata, and map entry overhead.
123+ //
124+ // We reserve an additional 10 MB in maxMemoryBytes for non-file data.
125+ //
126+ // The relationship between these parameters, as well as the overly-large and
127+ // unconfigurable 10 MB added on to maxMemory, is unfortunate but difficult to change
128+ // within the constraints of the API as documented.
129+ maxFileMemoryBytes := maxMemory
130+ maxMemoryBytes := maxMemory + int64(10<<20)
131+ if maxMemoryBytes <= 0 {
132 if maxMemory < 0 {
133- maxValueBytes = 0
134+ maxMemoryBytes = 0
135 } else {
136- maxValueBytes = math.MaxInt64
137+ maxMemoryBytes = math.MaxInt64
138 }
139 }
140 for {
141- p, err := r.NextPart()
142+ p, err := r.nextPart(false, maxMemoryBytes)
143 if err == io.EOF {
144 break
145 }
146@@ -64,16 +100,27 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
147 }
148 filename := p.FileName()
149
150+ // Multiple values for the same key (one map entry, longer slice) are cheaper
151+ // than the same number of values for different keys (many map entries), but
152+ // using a consistent per-value cost for overhead is simpler.
153+ maxMemoryBytes -= int64(len(name))
154+ maxMemoryBytes -= 100 // map overhead
155+ if maxMemoryBytes < 0 {
156+ // We can't actually take this path, since nextPart would already have
157+ // rejected the MIME headers for being too large. Check anyway.
158+ return nil, ErrMessageTooLarge
159+ }
160+
161 var b bytes.Buffer
162
163 if filename == "" {
164 // value, store as string in memory
165- n, err := io.CopyN(&b, p, maxValueBytes+1)
166+ n, err := io.CopyN(&b, p, maxMemoryBytes+1)
167 if err != nil && err != io.EOF {
168 return nil, err
169 }
170- maxValueBytes -= n
171- if maxValueBytes < 0 {
172+ maxMemoryBytes -= n
173+ if maxMemoryBytes < 0 {
174 return nil, ErrMessageTooLarge
175 }
176 form.Value[name] = append(form.Value[name], b.String())
177@@ -81,35 +128,45 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
178 }
179
180 // file, store in memory or on disk
181+ maxMemoryBytes -= mimeHeaderSize(p.Header)
182+ if maxMemoryBytes < 0 {
183+ return nil, ErrMessageTooLarge
184+ }
185 fh := &FileHeader{
186 Filename: filename,
187 Header: p.Header,
188 }
189- n, err := io.CopyN(&b, p, maxMemory+1)
190+ n, err := io.CopyN(&b, p, maxFileMemoryBytes+1)
191 if err != nil && err != io.EOF {
192 return nil, err
193 }
194- if n > maxMemory {
195- // too big, write to disk and flush buffer
196- file, err := ioutil.TempFile("", "multipart-")
197- if err != nil {
198- return nil, err
199+ if n > maxFileMemoryBytes {
200+ if file == nil {
201+ file, err = ioutil.TempFile(r.tempDir, "multipart-")
202+ if err != nil {
203+ return nil, err
204+ }
205 }
206+ numDiskFiles++
207 size, err := io.Copy(file, io.MultiReader(&b, p))
208- if cerr := file.Close(); err == nil {
209- err = cerr
210- }
211 if err != nil {
212- os.Remove(file.Name())
213 return nil, err
214 }
215 fh.tmpfile = file.Name()
216 fh.Size = size
217+ fh.tmpoff = fileOff
218+ fileOff += size
219+ if !combineFiles {
220+ if err := file.Close(); err != nil {
221+ return nil, err
222+ }
223+ file = nil
224+ }
225 } else {
226 fh.content = b.Bytes()
227 fh.Size = int64(len(fh.content))
228- maxMemory -= n
229- maxValueBytes -= n
230+ maxFileMemoryBytes -= n
231+ maxMemoryBytes -= n
232 }
233 form.File[name] = append(form.File[name], fh)
234 }
235@@ -117,6 +174,17 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
236 return form, nil
237 }
238
239+func mimeHeaderSize(h textproto.MIMEHeader) (size int64) {
240+ for k, vs := range h {
241+ size += int64(len(k))
242+ size += 100 // map entry overhead
243+ for _, v := range vs {
244+ size += int64(len(v))
245+ }
246+ }
247+ return size
248+}
249+
250 // Form is a parsed multipart form.
251 // Its File parts are stored either in memory or on disk,
252 // and are accessible via the *FileHeader's Open method.
253@@ -134,7 +202,7 @@ func (f *Form) RemoveAll() error {
254 for _, fh := range fhs {
255 if fh.tmpfile != "" {
256 e := os.Remove(fh.tmpfile)
257- if e != nil && err == nil {
258+ if e != nil && !errors.Is(e, os.ErrNotExist) && err == nil {
259 err = e
260 }
261 }
262@@ -149,15 +217,25 @@ type FileHeader struct {
263 Header textproto.MIMEHeader
264 Size int64
265
266- content []byte
267- tmpfile string
268+ content []byte
269+ tmpfile string
270+ tmpoff int64
271+ tmpshared bool
272 }
273
274 // Open opens and returns the FileHeader's associated File.
275 func (fh *FileHeader) Open() (File, error) {
276 if b := fh.content; b != nil {
277 r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
278- return sectionReadCloser{r}, nil
279+ return sectionReadCloser{r, nil}, nil
280+ }
281+ if fh.tmpshared {
282+ f, err := os.Open(fh.tmpfile)
283+ if err != nil {
284+ return nil, err
285+ }
286+ r := io.NewSectionReader(f, fh.tmpoff, fh.Size)
287+ return sectionReadCloser{r, f}, nil
288 }
289 return os.Open(fh.tmpfile)
290 }
291@@ -176,8 +254,12 @@ type File interface {
292
293 type sectionReadCloser struct {
294 *io.SectionReader
295+ io.Closer
296 }
297
298 func (rc sectionReadCloser) Close() error {
299+ if rc.Closer != nil {
300+ return rc.Closer.Close()
301+ }
302 return nil
303 }
304diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
305index e3a3a3e..5cded71 100644
306--- a/src/mime/multipart/formdata_test.go
307+++ b/src/mime/multipart/formdata_test.go
308@@ -6,8 +6,10 @@ package multipart
309
310 import (
311 "bytes"
312+ "fmt"
313 "io"
314 "math"
315+ "net/textproto"
316 "os"
317 "strings"
318 "testing"
319@@ -208,8 +210,8 @@ Content-Disposition: form-data; name="largetext"
320 maxMemory int64
321 err error
322 }{
323- {"smaller", 50, nil},
324- {"exact-fit", 25, nil},
325+ {"smaller", 50 + int64(len("largetext")) + 100, nil},
326+ {"exact-fit", 25 + int64(len("largetext")) + 100, nil},
327 {"too-large", 0, ErrMessageTooLarge},
328 }
329 for _, tc := range testCases {
330@@ -224,7 +226,7 @@ Content-Disposition: form-data; name="largetext"
331 defer f.RemoveAll()
332 }
333 if tc.err != err {
334- t.Fatalf("ReadForm error - got: %v; expected: %v", tc.err, err)
335+ t.Fatalf("ReadForm error - got: %v; expected: %v", err, tc.err)
336 }
337 if err == nil {
338 if g := f.Value["largetext"][0]; g != largeTextValue {
339@@ -234,3 +236,135 @@ Content-Disposition: form-data; name="largetext"
340 })
341 }
342 }
343+
344+// TestReadForm_MetadataTooLarge verifies that we account for the size of field names,
345+// MIME headers, and map entry overhead while limiting the memory consumption of parsed forms.
346+func TestReadForm_MetadataTooLarge(t *testing.T) {
347+ for _, test := range []struct {
348+ name string
349+ f func(*Writer)
350+ }{{
351+ name: "large name",
352+ f: func(fw *Writer) {
353+ name := strings.Repeat("a", 10<<20)
354+ w, _ := fw.CreateFormField(name)
355+ w.Write([]byte("value"))
356+ },
357+ }, {
358+ name: "large MIME header",
359+ f: func(fw *Writer) {
360+ h := make(textproto.MIMEHeader)
361+ h.Set("Content-Disposition", `form-data; name="a"`)
362+ h.Set("X-Foo", strings.Repeat("a", 10<<20))
363+ w, _ := fw.CreatePart(h)
364+ w.Write([]byte("value"))
365+ },
366+ }, {
367+ name: "many parts",
368+ f: func(fw *Writer) {
369+ for i := 0; i < 110000; i++ {
370+ w, _ := fw.CreateFormField("f")
371+ w.Write([]byte("v"))
372+ }
373+ },
374+ }} {
375+ t.Run(test.name, func(t *testing.T) {
376+ var buf bytes.Buffer
377+ fw := NewWriter(&buf)
378+ test.f(fw)
379+ if err := fw.Close(); err != nil {
380+ t.Fatal(err)
381+ }
382+ fr := NewReader(&buf, fw.Boundary())
383+ _, err := fr.ReadForm(0)
384+ if err != ErrMessageTooLarge {
385+ t.Errorf("fr.ReadForm() = %v, want ErrMessageTooLarge", err)
386+ }
387+ })
388+ }
389+}
390+
391+// TestReadForm_ManyFiles_Combined tests that a multipart form containing many files only
392+// results in a single on-disk file.
393+func TestReadForm_ManyFiles_Combined(t *testing.T) {
394+ const distinct = false
395+ testReadFormManyFiles(t, distinct)
396+}
397+
398+// TestReadForm_ManyFiles_Distinct tests that setting GODEBUG=multipartfiles=distinct
399+// results in every file in a multipart form being placed in a distinct on-disk file.
400+func TestReadForm_ManyFiles_Distinct(t *testing.T) {
401+ t.Setenv("GODEBUG", "multipartfiles=distinct")
402+ const distinct = true
403+ testReadFormManyFiles(t, distinct)
404+}
405+
406+func testReadFormManyFiles(t *testing.T, distinct bool) {
407+ var buf bytes.Buffer
408+ fw := NewWriter(&buf)
409+ const numFiles = 10
410+ for i := 0; i < numFiles; i++ {
411+ name := fmt.Sprint(i)
412+ w, err := fw.CreateFormFile(name, name)
413+ if err != nil {
414+ t.Fatal(err)
415+ }
416+ w.Write([]byte(name))
417+ }
418+ if err := fw.Close(); err != nil {
419+ t.Fatal(err)
420+ }
421+ fr := NewReader(&buf, fw.Boundary())
422+ fr.tempDir = t.TempDir()
423+ form, err := fr.ReadForm(0)
424+ if err != nil {
425+ t.Fatal(err)
426+ }
427+ for i := 0; i < numFiles; i++ {
428+ name := fmt.Sprint(i)
429+ if got := len(form.File[name]); got != 1 {
430+ t.Fatalf("form.File[%q] has %v entries, want 1", name, got)
431+ }
432+ fh := form.File[name][0]
433+ file, err := fh.Open()
434+ if err != nil {
435+ t.Fatalf("form.File[%q].Open() = %v", name, err)
436+ }
437+ if distinct {
438+ if _, ok := file.(*os.File); !ok {
439+ t.Fatalf("form.File[%q].Open: %T, want *os.File", name, file)
440+ }
441+ }
442+ got, err := io.ReadAll(file)
443+ file.Close()
444+ if string(got) != name || err != nil {
445+ t.Fatalf("read form.File[%q]: %q, %v; want %q, nil", name, string(got), err, name)
446+ }
447+ }
448+ dir, err := os.Open(fr.tempDir)
449+ if err != nil {
450+ t.Fatal(err)
451+ }
452+ defer dir.Close()
453+ names, err := dir.Readdirnames(0)
454+ if err != nil {
455+ t.Fatal(err)
456+ }
457+ wantNames := 1
458+ if distinct {
459+ wantNames = numFiles
460+ }
461+ if len(names) != wantNames {
462+ t.Fatalf("temp dir contains %v files; want 1", len(names))
463+ }
464+ if err := form.RemoveAll(); err != nil {
465+ t.Fatalf("form.RemoveAll() = %v", err)
466+ }
467+ names, err = dir.Readdirnames(0)
468+ if err != nil {
469+ t.Fatal(err)
470+ }
471+ if len(names) != 0 {
472+ t.Fatalf("temp dir contains %v files; want 0", len(names))
473+ }
474+}
475diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go
476index 1750300..958cef8 100644
477--- a/src/mime/multipart/multipart.go
478+++ b/src/mime/multipart/multipart.go
479@@ -121,12 +121,12 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) {
480 return n, r.err
481 }
482
483-func newPart(mr *Reader, rawPart bool) (*Part, error) {
484+func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) {
485 bp := &Part{
486 Header: make(map[string][]string),
487 mr: mr,
488 }
489- if err := bp.populateHeaders(); err != nil {
490+ if err := bp.populateHeaders(maxMIMEHeaderSize); err != nil {
491 return nil, err
492 }
493 bp.r = partReader{bp}
494@@ -142,12 +142,16 @@ func newPart(mr *Reader, rawPart bool) (*Part, error) {
495 return bp, nil
496 }
497
498-func (bp *Part) populateHeaders() error {
499+func (bp *Part) populateHeaders(maxMIMEHeaderSize int64) error {
500 r := textproto.NewReader(bp.mr.bufReader)
501- header, err := r.ReadMIMEHeader()
502+ header, err := readMIMEHeader(r, maxMIMEHeaderSize)
503 if err == nil {
504 bp.Header = header
505 }
506+ // TODO: Add a distinguishable error to net/textproto.
507+ if err != nil && err.Error() == "message too large" {
508+ err = ErrMessageTooLarge
509+ }
510 return err
511 }
512
513@@ -287,6 +291,7 @@ func (p *Part) Close() error {
514 // isn't supported.
515 type Reader struct {
516 bufReader *bufio.Reader
517+ tempDir string // used in tests
518
519 currentPart *Part
520 partsRead int
521@@ -297,6 +302,10 @@ type Reader struct {
522 dashBoundary []byte // "--boundary"
523 }
524
525+// maxMIMEHeaderSize is the maximum size of a MIME header we will parse,
526+// including header keys, values, and map overhead.
527+const maxMIMEHeaderSize = 10 << 20
528+
529 // NextPart returns the next part in the multipart or an error.
530 // When there are no more parts, the error io.EOF is returned.
531 //
532@@ -304,7 +313,7 @@ type Reader struct {
533 // has a value of "quoted-printable", that header is instead
534 // hidden and the body is transparently decoded during Read calls.
535 func (r *Reader) NextPart() (*Part, error) {
536- return r.nextPart(false)
537+ return r.nextPart(false, maxMIMEHeaderSize)
538 }
539
540 // NextRawPart returns the next part in the multipart or an error.
541@@ -313,10 +322,10 @@ func (r *Reader) NextPart() (*Part, error) {
542 // Unlike NextPart, it does not have special handling for
543 // "Content-Transfer-Encoding: quoted-printable".
544 func (r *Reader) NextRawPart() (*Part, error) {
545- return r.nextPart(true)
546+ return r.nextPart(true, maxMIMEHeaderSize)
547 }
548
549-func (r *Reader) nextPart(rawPart bool) (*Part, error) {
550+func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) {
551 if r.currentPart != nil {
552 r.currentPart.Close()
553 }
554@@ -341,7 +350,7 @@ func (r *Reader) nextPart(rawPart bool) (*Part, error) {
555
556 if r.isBoundaryDelimiterLine(line) {
557 r.partsRead++
558- bp, err := newPart(r, rawPart)
559+ bp, err := newPart(r, rawPart, maxMIMEHeaderSize)
560 if err != nil {
561 return nil, err
562 }
563diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go
564new file mode 100644
565index 0000000..6836928
566--- /dev/null
567+++ b/src/mime/multipart/readmimeheader.go
568@@ -0,0 +1,14 @@
569+// Copyright 2023 The Go Authors. All rights reserved.
570+// Use of this source code is governed by a BSD-style
571+// license that can be found in the LICENSE file.
572+package multipart
573+
574+import (
575+ "net/textproto"
576+ _ "unsafe" // for go:linkname
577+)
578+
579+// readMIMEHeader is defined in package net/textproto.
580+//
581+//go:linkname readMIMEHeader net/textproto.readMIMEHeader
582+func readMIMEHeader(r *textproto.Reader, lim int64) (textproto.MIMEHeader, error)
583diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
584index 94133ee..170d3f5 100644
585--- a/src/net/http/request_test.go
586+++ b/src/net/http/request_test.go
587@@ -962,7 +962,7 @@ func testMissingFile(t *testing.T, req *Request) {
588 t.Errorf("FormFile file = %v, want nil", f)
589 }
590 if fh != nil {
591- t.Errorf("FormFile file header = %q, want nil", fh)
592+ t.Errorf("FormFile file header = %v, want nil", fh)
593 }
594 if err != ErrMissingFile {
595 t.Errorf("FormFile err = %q, want ErrMissingFile", err)
596diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
597index f63f5ec..96553fb 100644
598--- a/src/net/textproto/reader.go
599+++ b/src/net/textproto/reader.go
600@@ -7,9 +7,11 @@ package textproto
601 import (
602 "bufio"
603 "bytes"
604+ "errors"
605 "fmt"
606 "io"
607 "io/ioutil"
608+ "math"
609 "strconv"
610 "strings"
611 "sync"
612@@ -482,6 +484,12 @@ func (r *Reader) ReadDotLines() ([]string, error) {
613 // }
614 //
615 func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
616+ return readMIMEHeader(r, math.MaxInt64)
617+}
618+
619+// readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size.
620+// It is called by the mime/multipart package.
621+func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
622 // Avoid lots of small slice allocations later by allocating one
623 // large one ahead of time which we'll cut up into smaller
624 // slices. If this isn't big enough later, we allocate small ones.
625@@ -525,6 +533,15 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
626 continue
627 }
628
629+ // backport 5c55ac9bf1e5f779220294c843526536605f42ab
630+ //
631+ // value is computed as
632+ // value := string(bytes.TrimLeft(v, " \t"))
633+ //
634+ // in the original patch from 1.19. This relies on
635+ // 'v' which does not exist in 1.14. We leave the
636+ // 1.14 method unchanged.
637+
638 // Skip initial spaces in value.
639 i++ // skip colon
640 for i < len(kv) && (kv[i] == ' ' || kv[i] == '\t') {
641@@ -533,6 +550,16 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
642 value := string(kv[i:])
643
644 vv := m[key]
645+ if vv == nil {
646+ lim -= int64(len(key))
647+ lim -= 100 // map entry overhead
648+ }
649+ lim -= int64(len(value))
650+ if lim < 0 {
651+ // TODO: This should be a distinguishable error (ErrMessageTooLarge)
652+ // to allow mime/multipart to detect it.
653+ return m, errors.New("message too large")
654+ }
655 if vv == nil && len(strs) > 0 {
656 // More than likely this will be a single-element key.
657 // Most headers aren't multi-valued.
658--
6592.25.1
660