summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/go
diff options
context:
space:
mode:
authorVijay Anusuri <vanusuri@mvista.com>2023-09-25 14:15:46 +0530
committerSteve Sakoman <steve@sakoman.com>2023-09-29 04:29:01 -1000
commitea9b55c8588ce5d7f9d8a1aa317d3c5b9f966dd8 (patch)
tree3273bfb80942612ea35d381363f158ddbe8577e1 /meta/recipes-devtools/go
parent0734868d9d9365c63cadf51ff8272fb0662e11a7 (diff)
downloadpoky-ea9b55c8588ce5d7f9d8a1aa317d3c5b9f966dd8.tar.gz
go: Backport fix for CVE-2022-41725 and CVE-2023-24536
Upstream-commit: https://github.com/golang/go/commit/874b3132a84cf76da6a48978826c04c380a37a50 & https://github.com/golang/go/commit/4e5a313524da62600eb59dbf98624cfe946456f8 & https://github.com/golang/go/commit/5246fa5e75b129a7dbd9722aa4de0cbaf7ceae43 & https://github.com/golang/go/commit/5c55ac9bf1e5f779220294c843526536605f42ab & https://github.com/golang/go/commit/ef41a4e2face45e580c5836eaebd51629fc23f15 & https://github.com/golang/go/commit/7a359a651c7ebdb29e0a1c03102fce793e9f58f0 & https://github.com/golang/go/commit/7917b5f31204528ea72e0629f0b7d52b35b27538 (From OE-Core rev: 532eb2c57fb1817999a857fc71db4438717ccadb) Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
Diffstat (limited to 'meta/recipes-devtools/go')
-rw-r--r--meta/recipes-devtools/go/go-1.14.inc7
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre1.patch85
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch97
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch98
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch660
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch134
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch184
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch349
8 files changed, 1614 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14.inc b/meta/recipes-devtools/go/go-1.14.inc
index 20377e095b..784b502f46 100644
--- a/meta/recipes-devtools/go/go-1.14.inc
+++ b/meta/recipes-devtools/go/go-1.14.inc
@@ -70,6 +70,13 @@ SRC_URI += "\
70 file://CVE-2023-29400.patch \ 70 file://CVE-2023-29400.patch \
71 file://CVE-2023-29406.patch \ 71 file://CVE-2023-29406.patch \
72 file://CVE-2023-29409.patch \ 72 file://CVE-2023-29409.patch \
73 file://CVE-2022-41725-pre1.patch \
74 file://CVE-2022-41725-pre2.patch \
75 file://CVE-2022-41725-pre3.patch \
76 file://CVE-2022-41725.patch \
77 file://CVE-2023-24536_1.patch \
78 file://CVE-2023-24536_2.patch \
79 file://CVE-2023-24536_3.patch \
73" 80"
74 81
75SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" 82SRC_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-2022-41725-pre1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre1.patch
new file mode 100644
index 0000000000..37ebc41947
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre1.patch
@@ -0,0 +1,85 @@
1From 874b3132a84cf76da6a48978826c04c380a37a50 Mon Sep 17 00:00:00 2001
2From: avivklas <avivklas@gmail.com>
3Date: Fri, 7 Aug 2020 21:50:12 +0300
4Subject: [PATCH] mime/multipart: return overflow errors in Reader.ReadForm
5
6Updates Reader.ReadForm to check for overflow errors that may
7result from a leeway addition of 10MiB to the input argument
8maxMemory.
9
10Fixes #40430
11
12Change-Id: I510b8966c95c51d04695ba9d08fcfe005fd11a5d
13Reviewed-on: https://go-review.googlesource.com/c/go/+/247477
14Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
15Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
16Trust: Emmanuel Odeke <emm.odeke@gmail.com>
17TryBot-Result: Go Bot <gobot@golang.org>
18Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
19
20Upstream-Status: Backport [https://github.com/golang/go/commit/874b3132a84cf76da6a48978826c04c380a37a50]
21CVE: CVE-2022-41725 #Dependency Patch1
22Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
23---
24 src/mime/multipart/formdata.go | 4 ++++
25 src/mime/multipart/formdata_test.go | 18 ++++++++++++++++++
26 2 files changed, 22 insertions(+)
27
28diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
29index 832d0ad693666..4eb31012941ac 100644
30--- a/src/mime/multipart/formdata.go
31+++ b/src/mime/multipart/formdata.go
32@@ -7,6 +7,7 @@ package multipart
33 import (
34 "bytes"
35 "errors"
36+ "fmt"
37 "io"
38 "io/ioutil"
39 "net/textproto"
40@@ -41,6 +42,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
41
42 // Reserve an additional 10 MB for non-file parts.
43 maxValueBytes := maxMemory + int64(10<<20)
44+ if maxValueBytes <= 0 {
45+ return nil, fmt.Errorf("multipart: integer overflow from maxMemory(%d) + 10MiB for non-file parts", maxMemory)
46+ }
47 for {
48 p, err := r.NextPart()
49 if err == io.EOF {
50diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
51index 7d756c8c244a0..7112e0d3727fe 100644
52--- a/src/mime/multipart/formdata_test.go
53+++ b/src/mime/multipart/formdata_test.go
54@@ -7,6 +7,7 @@ package multipart
55 import (
56 "bytes"
57 "io"
58+ "math"
59 "os"
60 "strings"
61 "testing"
62@@ -52,6 +53,23 @@ func TestReadFormWithNamelessFile(t *testing.T) {
63 }
64 }
65
66+// Issue 40430: Ensure that we report integer overflows in additions of maxMemory,
67+// instead of silently and subtly failing without indication.
68+func TestReadFormMaxMemoryOverflow(t *testing.T) {
69+ b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
70+ r := NewReader(b, boundary)
71+ f, err := r.ReadForm(math.MaxInt64)
72+ if err == nil {
73+ t.Fatal("Unexpected a non-nil error")
74+ }
75+ if f != nil {
76+ t.Fatalf("Unexpected returned a non-nil form: %v\n", f)
77+ }
78+ if g, w := err.Error(), "integer overflow from maxMemory"; !strings.Contains(g, w) {
79+ t.Errorf(`Error mismatch\n%q\ndid not contain\n%q`, g, w)
80+ }
81+}
82+
83 func TestReadFormWithTextContentType(t *testing.T) {
84 // From https://github.com/golang/go/issues/24041
85 b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch
new file mode 100644
index 0000000000..b951ee893e
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch
@@ -0,0 +1,97 @@
1From 4e5a313524da62600eb59dbf98624cfe946456f8 Mon Sep 17 00:00:00 2001
2From: Emmanuel T Odeke <emmanuel@orijtech.com>
3Date: Tue, 20 Oct 2020 04:11:12 -0700
4Subject: [PATCH] net/http: test that ParseMultipartForm catches overflows
5
6Tests that if the combination of:
7* HTTP multipart file payload size
8* ParseMultipartForm's maxMemory parameter
9* the internal leeway buffer size of 10MiB
10
11overflows, then we'll report an overflow instead of silently
12passing.
13
14Reapplies and fixes CL 254977, which was reverted in CL 263658.
15
16The prior test lacked a res.Body.Close(), so fixed that and
17added a leaked Transport check to verify correctness.
18
19Updates 40430.
20
21Change-Id: I3c0f7ef43d621f6eb00f07755f04f9f36c51f98f
22Reviewed-on: https://go-review.googlesource.com/c/go/+/263817
23Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
24TryBot-Result: Go Bot <gobot@golang.org>
25Reviewed-by: Bryan C. Mills <bcmills@google.com>
26Trust: Damien Neil <dneil@google.com>
27
28Upstream-Status: Backport [https://github.com/golang/go/commit/4e5a313524da62600eb59dbf98624cfe946456f8]
29CVE: CVE-2022-41725 #Dependency Patch2
30Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
31---
32 src/net/http/request_test.go | 45 ++++++++++++++++++++++++++++++++++++
33 1 file changed, 45 insertions(+)
34
35diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
36index b4ef472e71229..19526b9ad791a 100644
37--- a/src/net/http/request_test.go
38+++ b/src/net/http/request_test.go
39@@ -13,6 +13,7 @@ import (
40 "fmt"
41 "io"
42 "io/ioutil"
43+ "math"
44 "mime/multipart"
45 . "net/http"
46 "net/http/httptest"
47@@ -245,6 +246,50 @@ func TestParseMultipartForm(t *testing.T) {
48 }
49 }
50
51+// Issue #40430: Test that if maxMemory for ParseMultipartForm when combined with
52+// the payload size and the internal leeway buffer size of 10MiB overflows, that we
53+// correctly return an error.
54+func TestMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T) {
55+ defer afterTest(t)
56+
57+ payloadSize := 1 << 10
58+ cst := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
59+ // The combination of:
60+ // MaxInt64 + payloadSize + (internal spare of 10MiB)
61+ // triggers the overflow. See issue https://golang.org/issue/40430/
62+ if err := req.ParseMultipartForm(math.MaxInt64); err != nil {
63+ Error(rw, err.Error(), StatusBadRequest)
64+ return
65+ }
66+ }))
67+ defer cst.Close()
68+ fBuf := new(bytes.Buffer)
69+ mw := multipart.NewWriter(fBuf)
70+ mf, err := mw.CreateFormFile("file", "myfile.txt")
71+ if err != nil {
72+ t.Fatal(err)
73+ }
74+ if _, err := mf.Write(bytes.Repeat([]byte("abc"), payloadSize)); err != nil {
75+ t.Fatal(err)
76+ }
77+ if err := mw.Close(); err != nil {
78+ t.Fatal(err)
79+ }
80+ req, err := NewRequest("POST", cst.URL, fBuf)
81+ if err != nil {
82+ t.Fatal(err)
83+ }
84+ req.Header.Set("Content-Type", mw.FormDataContentType())
85+ res, err := cst.Client().Do(req)
86+ if err != nil {
87+ t.Fatal(err)
88+ }
89+ res.Body.Close()
90+ if g, w := res.StatusCode, StatusBadRequest; g != w {
91+ t.Fatalf("Status code mismatch: got %d, want %d", g, w)
92+ }
93+}
94+
95 func TestRedirect_h1(t *testing.T) { testRedirect(t, h1Mode) }
96 func TestRedirect_h2(t *testing.T) { testRedirect(t, h2Mode) }
97 func testRedirect(t *testing.T, h2 bool) {
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch
new file mode 100644
index 0000000000..767225b888
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch
@@ -0,0 +1,98 @@
1From 5246fa5e75b129a7dbd9722aa4de0cbaf7ceae43 Mon Sep 17 00:00:00 2001
2From: Russ Cox <rsc@golang.org>
3Date: Thu, 3 Dec 2020 09:45:07 -0500
4Subject: [PATCH] mime/multipart: handle ReadForm(math.MaxInt64) better
5
6Returning an error about integer overflow is needlessly pedantic.
7The meaning of ReadForm(MaxInt64) is easily understood
8(accept a lot of data) and can be implemented.
9
10Fixes #40430.
11
12Change-Id: I8a522033dd9a2f9ad31dd2ad82cf08d553736ab9
13Reviewed-on: https://go-review.googlesource.com/c/go/+/275112
14Trust: Russ Cox <rsc@golang.org>
15Run-TryBot: Russ Cox <rsc@golang.org>
16TryBot-Result: Go Bot <gobot@golang.org>
17Reviewed-by: Ian Lance Taylor <iant@golang.org>
18
19Upstream-Status: Backport [https://github.com/golang/go/commit/5246fa5e75b129a7dbd9722aa4de0cbaf7ceae43]
20CVE: CVE-2022-41725 #Dependency Patch3
21Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
22---
23 src/mime/multipart/formdata.go | 8 ++++++--
24 src/mime/multipart/formdata_test.go | 14 +++++---------
25 src/net/http/request_test.go | 2 +-
26 3 files changed, 12 insertions(+), 12 deletions(-)
27
28diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
29index 4eb31012941ac..9c42ea8c023b5 100644
30--- a/src/mime/multipart/formdata.go
31+++ b/src/mime/multipart/formdata.go
32@@ -7,9 +7,9 @@ package multipart
33 import (
34 "bytes"
35 "errors"
36- "fmt"
37 "io"
38 "io/ioutil"
39+ "math"
40 "net/textproto"
41 "os"
42 )
43@@ -43,7 +43,11 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
44 // Reserve an additional 10 MB for non-file parts.
45 maxValueBytes := maxMemory + int64(10<<20)
46 if maxValueBytes <= 0 {
47- return nil, fmt.Errorf("multipart: integer overflow from maxMemory(%d) + 10MiB for non-file parts", maxMemory)
48+ if maxMemory < 0 {
49+ maxValueBytes = 0
50+ } else {
51+ maxValueBytes = math.MaxInt64
52+ }
53 }
54 for {
55 p, err := r.NextPart()
56diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
57index 7112e0d3727fe..e3a3a3eae8e15 100644
58--- a/src/mime/multipart/formdata_test.go
59+++ b/src/mime/multipart/formdata_test.go
60@@ -53,20 +53,16 @@ func TestReadFormWithNamelessFile(t *testing.T) {
61 }
62 }
63
64-// Issue 40430: Ensure that we report integer overflows in additions of maxMemory,
65-// instead of silently and subtly failing without indication.
66+// Issue 40430: Handle ReadForm(math.MaxInt64)
67 func TestReadFormMaxMemoryOverflow(t *testing.T) {
68 b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
69 r := NewReader(b, boundary)
70 f, err := r.ReadForm(math.MaxInt64)
71- if err == nil {
72- t.Fatal("Unexpected a non-nil error")
73- }
74- if f != nil {
75- t.Fatalf("Unexpected returned a non-nil form: %v\n", f)
76+ if err != nil {
77+ t.Fatalf("ReadForm(MaxInt64): %v", err)
78 }
79- if g, w := err.Error(), "integer overflow from maxMemory"; !strings.Contains(g, w) {
80- t.Errorf(`Error mismatch\n%q\ndid not contain\n%q`, g, w)
81+ if f == nil {
82+ t.Fatal("ReadForm(MaxInt64): missing form")
83 }
84 }
85
86diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
87index 19526b9ad791a..689498e19d5dd 100644
88--- a/src/net/http/request_test.go
89+++ b/src/net/http/request_test.go
90@@ -285,7 +285,7 @@ func TestMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T) {
91 t.Fatal(err)
92 }
93 res.Body.Close()
94- if g, w := res.StatusCode, StatusBadRequest; g != w {
95+ if g, w := res.StatusCode, StatusOK; g != w {
96 t.Fatalf("Status code mismatch: got %d, want %d", g, w)
97 }
98 }
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
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch
new file mode 100644
index 0000000000..39e1304fbd
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch
@@ -0,0 +1,134 @@
1From ef41a4e2face45e580c5836eaebd51629fc23f15 Mon Sep 17 00:00:00 2001
2From: Damien Neil <dneil@google.com>
3Date: Thu, 16 Mar 2023 14:18:04 -0700
4Subject: [PATCH] [release-branch.go1.19] mime/multipart: avoid excessive copy
5 buffer allocations in ReadForm
6
7When copying form data to disk with io.Copy,
8allocate only one copy buffer and reuse it rather than
9creating two buffers per file (one from io.multiReader.WriteTo,
10and a second one from os.File.ReadFrom).
11
12Thanks to Jakob Ackermann (@das7pad) for reporting this issue.
13
14For CVE-2023-24536
15For #59153
16For #59269
17
18Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802453
19Run-TryBot: Damien Neil <dneil@google.com>
20Reviewed-by: Julie Qiu <julieqiu@google.com>
21Reviewed-by: Roland Shoemaker <bracewell@google.com>
22Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802395
23Run-TryBot: Roland Shoemaker <bracewell@google.com>
24Reviewed-by: Damien Neil <dneil@google.com>
25Change-Id: Ie405470c92abffed3356913b37d813e982c96c8b
26Reviewed-on: https://go-review.googlesource.com/c/go/+/481983
27Run-TryBot: Michael Knyszek <mknyszek@google.com>
28TryBot-Result: Gopher Robot <gobot@golang.org>
29Auto-Submit: Michael Knyszek <mknyszek@google.com>
30Reviewed-by: Matthew Dempsky <mdempsky@google.com>
31
32Upstream-Status: Backport [https://github.com/golang/go/commit/ef41a4e2face45e580c5836eaebd51629fc23f15]
33CVE: CVE-2023-24536
34Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
35---
36 src/mime/multipart/formdata.go | 15 +++++++--
37 src/mime/multipart/formdata_test.go | 49 +++++++++++++++++++++++++++++
38 2 files changed, 61 insertions(+), 3 deletions(-)
39
40diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
41index a7d4ca97f0484..975dcb6b26db4 100644
42--- a/src/mime/multipart/formdata.go
43+++ b/src/mime/multipart/formdata.go
44@@ -84,6 +84,7 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
45 maxMemoryBytes = math.MaxInt64
46 }
47 }
48+ var copyBuf []byte
49 for {
50 p, err := r.nextPart(false, maxMemoryBytes)
51 if err == io.EOF {
52@@ -147,14 +148,22 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
53 }
54 }
55 numDiskFiles++
56- size, err := io.Copy(file, io.MultiReader(&b, p))
57+ if _, err := file.Write(b.Bytes()); err != nil {
58+ return nil, err
59+ }
60+ if copyBuf == nil {
61+ copyBuf = make([]byte, 32*1024) // same buffer size as io.Copy uses
62+ }
63+ // os.File.ReadFrom will allocate its own copy buffer if we let io.Copy use it.
64+ type writerOnly struct{ io.Writer }
65+ remainingSize, err := io.CopyBuffer(writerOnly{file}, p, copyBuf)
66 if err != nil {
67 return nil, err
68 }
69 fh.tmpfile = file.Name()
70- fh.Size = size
71+ fh.Size = int64(b.Len()) + remainingSize
72 fh.tmpoff = fileOff
73- fileOff += size
74+ fileOff += fh.Size
75 if !combineFiles {
76 if err := file.Close(); err != nil {
77 return nil, err
78diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
79index 5cded7170c6b8..f5b56083b2377 100644
80--- a/src/mime/multipart/formdata_test.go
81+++ b/src/mime/multipart/formdata_test.go
82@@ -368,3 +368,52 @@ func testReadFormManyFiles(t *testing.T, distinct bool) {
83 t.Fatalf("temp dir contains %v files; want 0", len(names))
84 }
85 }
86+
87+func BenchmarkReadForm(b *testing.B) {
88+ for _, test := range []struct {
89+ name string
90+ form func(fw *Writer, count int)
91+ }{{
92+ name: "fields",
93+ form: func(fw *Writer, count int) {
94+ for i := 0; i < count; i++ {
95+ w, _ := fw.CreateFormField(fmt.Sprintf("field%v", i))
96+ fmt.Fprintf(w, "value %v", i)
97+ }
98+ },
99+ }, {
100+ name: "files",
101+ form: func(fw *Writer, count int) {
102+ for i := 0; i < count; i++ {
103+ w, _ := fw.CreateFormFile(fmt.Sprintf("field%v", i), fmt.Sprintf("file%v", i))
104+ fmt.Fprintf(w, "value %v", i)
105+ }
106+ },
107+ }} {
108+ b.Run(test.name, func(b *testing.B) {
109+ for _, maxMemory := range []int64{
110+ 0,
111+ 1 << 20,
112+ } {
113+ var buf bytes.Buffer
114+ fw := NewWriter(&buf)
115+ test.form(fw, 10)
116+ if err := fw.Close(); err != nil {
117+ b.Fatal(err)
118+ }
119+ b.Run(fmt.Sprintf("maxMemory=%v", maxMemory), func(b *testing.B) {
120+ b.ReportAllocs()
121+ for i := 0; i < b.N; i++ {
122+ fr := NewReader(bytes.NewReader(buf.Bytes()), fw.Boundary())
123+ form, err := fr.ReadForm(maxMemory)
124+ if err != nil {
125+ b.Fatal(err)
126+ }
127+ form.RemoveAll()
128+ }
129+
130+ })
131+ }
132+ })
133+ }
134+}
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch
new file mode 100644
index 0000000000..9ba5114c82
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch
@@ -0,0 +1,184 @@
1From 7a359a651c7ebdb29e0a1c03102fce793e9f58f0 Mon Sep 17 00:00:00 2001
2From: Damien Neil <dneil@google.com>
3Date: Thu, 16 Mar 2023 16:56:12 -0700
4Subject: [PATCH] [release-branch.go1.19] net/textproto, mime/multipart:
5 improve accounting of non-file data
6
7For requests containing large numbers of small parts,
8memory consumption of a parsed form could be about 250%
9over the estimated size.
10
11When considering the size of parsed forms, account for the size of
12FileHeader structs and increase the estimate of memory consumed by
13map entries.
14
15Thanks to Jakob Ackermann (@das7pad) for reporting this issue.
16
17For CVE-2023-24536
18For #59153
19For #59269
20
21Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802454
22Run-TryBot: Damien Neil <dneil@google.com>
23Reviewed-by: Roland Shoemaker <bracewell@google.com>
24Reviewed-by: Julie Qiu <julieqiu@google.com>
25Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802396
26Run-TryBot: Roland Shoemaker <bracewell@google.com>
27Reviewed-by: Damien Neil <dneil@google.com>
28Change-Id: I31bc50e9346b4eee6fbe51a18c3c57230cc066db
29Reviewed-on: https://go-review.googlesource.com/c/go/+/481984
30Reviewed-by: Matthew Dempsky <mdempsky@google.com>
31Auto-Submit: Michael Knyszek <mknyszek@google.com>
32TryBot-Result: Gopher Robot <gobot@golang.org>
33Run-TryBot: Michael Knyszek <mknyszek@google.com>
34
35Upstream-Status: Backport [https://github.com/golang/go/commit/7a359a651c7ebdb29e0a1c03102fce793e9f58f0]
36CVE: CVE-2023-24536
37Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
38---
39 src/mime/multipart/formdata.go | 9 +++--
40 src/mime/multipart/formdata_test.go | 55 ++++++++++++-----------------
41 src/net/textproto/reader.go | 8 ++++-
42 3 files changed, 37 insertions(+), 35 deletions(-)
43
44diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
45index 975dcb6b26db4..3f6ff697ca608 100644
46--- a/src/mime/multipart/formdata.go
47+++ b/src/mime/multipart/formdata.go
48@@ -103,8 +103,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
49 // Multiple values for the same key (one map entry, longer slice) are cheaper
50 // than the same number of values for different keys (many map entries), but
51 // using a consistent per-value cost for overhead is simpler.
52+ const mapEntryOverhead = 200
53 maxMemoryBytes -= int64(len(name))
54- maxMemoryBytes -= 100 // map overhead
55+ maxMemoryBytes -= mapEntryOverhead
56 if maxMemoryBytes < 0 {
57 // We can't actually take this path, since nextPart would already have
58 // rejected the MIME headers for being too large. Check anyway.
59@@ -128,7 +129,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
60 }
61
62 // file, store in memory or on disk
63+ const fileHeaderSize = 100
64 maxMemoryBytes -= mimeHeaderSize(p.Header)
65+ maxMemoryBytes -= mapEntryOverhead
66+ maxMemoryBytes -= fileHeaderSize
67 if maxMemoryBytes < 0 {
68 return nil, ErrMessageTooLarge
69 }
70@@ -183,9 +187,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
71 }
72
73 func mimeHeaderSize(h textproto.MIMEHeader) (size int64) {
74+ size = 400
75 for k, vs := range h {
76 size += int64(len(k))
77- size += 100 // map entry overhead
78+ size += 200 // map entry overhead
79 for _, v := range vs {
80 size += int64(len(v))
81 }
82diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
83index f5b56083b2377..8ed26e0c34081 100644
84--- a/src/mime/multipart/formdata_test.go
85+++ b/src/mime/multipart/formdata_test.go
86@@ -192,10 +192,10 @@ func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) {
87 // TestReadForm_NonFileMaxMemory asserts that the ReadForm maxMemory limit is applied
88 // while processing non-file form data as well as file form data.
89 func TestReadForm_NonFileMaxMemory(t *testing.T) {
90- n := 10<<20 + 25
91 if testing.Short() {
92- n = 10<<10 + 25
93+ t.Skip("skipping in -short mode")
94 }
95+ n := 10 << 20
96 largeTextValue := strings.Repeat("1", n)
97 message := `--MyBoundary
98 Content-Disposition: form-data; name="largetext"
99@@ -203,38 +203,29 @@ Content-Disposition: form-data; name="largetext"
100 ` + largeTextValue + `
101 --MyBoundary--
102 `
103-
104 testBody := strings.ReplaceAll(message, "\n", "\r\n")
105- testCases := []struct {
106- name string
107- maxMemory int64
108- err error
109- }{
110- {"smaller", 50 + int64(len("largetext")) + 100, nil},
111- {"exact-fit", 25 + int64(len("largetext")) + 100, nil},
112- {"too-large", 0, ErrMessageTooLarge},
113- }
114- for _, tc := range testCases {
115- t.Run(tc.name, func(t *testing.T) {
116- if tc.maxMemory == 0 && testing.Short() {
117- t.Skip("skipping in -short mode")
118- }
119- b := strings.NewReader(testBody)
120- r := NewReader(b, boundary)
121- f, err := r.ReadForm(tc.maxMemory)
122- if err == nil {
123- defer f.RemoveAll()
124- }
125- if tc.err != err {
126- t.Fatalf("ReadForm error - got: %v; expected: %v", err, tc.err)
127- }
128- if err == nil {
129- if g := f.Value["largetext"][0]; g != largeTextValue {
130- t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue))
131- }
132- }
133- })
134+ // Try parsing the form with increasing maxMemory values.
135+ // Changes in how we account for non-file form data may cause the exact point
136+ // where we change from rejecting the form as too large to accepting it to vary,
137+ // but we should see both successes and failures.
138+ const failWhenMaxMemoryLessThan = 128
139+ for maxMemory := int64(0); maxMemory < failWhenMaxMemoryLessThan*2; maxMemory += 16 {
140+ b := strings.NewReader(testBody)
141+ r := NewReader(b, boundary)
142+ f, err := r.ReadForm(maxMemory)
143+ if err != nil {
144+ continue
145+ }
146+ if g := f.Value["largetext"][0]; g != largeTextValue {
147+ t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue))
148+ }
149+ f.RemoveAll()
150+ if maxMemory < failWhenMaxMemoryLessThan {
151+ t.Errorf("ReadForm(%v): no error, expect to hit memory limit when maxMemory < %v", maxMemory, failWhenMaxMemoryLessThan)
152+ }
153+ return
154 }
155+ t.Errorf("ReadForm(x) failed for x < 1024, expect success")
156 }
157
158 // TestReadForm_MetadataTooLarge verifies that we account for the size of field names,
159diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
160index 9a21777df8be0..c1284fde25eb7 100644
161--- a/src/net/textproto/reader.go
162+++ b/src/net/textproto/reader.go
163@@ -503,6 +503,12 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
164
165 m := make(MIMEHeader, hint)
166
167+ // Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry.
168+ // Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large
169+ // MIMEHeaders average about 200 bytes per entry.
170+ lim -= 400
171+ const mapEntryOverhead = 200
172+
173 // The first line cannot start with a leading space.
174 if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') {
175 line, err := r.readLineSlice()
176@@ -538,7 +544,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
177 vv := m[key]
178 if vv == nil {
179 lim -= int64(len(key))
180- lim -= 100 // map entry overhead
181+ lim -= mapEntryOverhead
182 }
183 lim -= int64(len(value))
184 if lim < 0 {
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 @@
1From 7917b5f31204528ea72e0629f0b7d52b35b27538 Mon Sep 17 00:00:00 2001
2From: Damien Neil <dneil@google.com>
3Date: Mon, 20 Mar 2023 10:43:19 -0700
4Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit parsed mime message sizes
5
6The parsed forms of MIME headers and multipart forms can consume
7substantially more memory than the size of the input data.
8A malicious input containing a very large number of headers or
9form parts can cause excessively large memory allocations.
10
11Set limits on the size of MIME data:
12
13Reader.NextPart and Reader.NextRawPart limit the the number
14of headers in a part to 10000.
15
16Reader.ReadForm limits the total number of headers in all
17FileHeaders to 10000.
18
19Both of these limits may be set with with
20GODEBUG=multipartmaxheaders=<values>.
21
22Reader.ReadForm limits the number of parts in a form to 1000.
23This limit may be set with GODEBUG=multipartmaxparts=<value>.
24
25Thanks for Jakob Ackermann (@das7pad) for reporting this issue.
26
27For CVE-2023-24536
28For #59153
29For #59269
30
31Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802455
32Run-TryBot: Damien Neil <dneil@google.com>
33Reviewed-by: Roland Shoemaker <bracewell@google.com>
34Reviewed-by: Julie Qiu <julieqiu@google.com>
35Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1801087
36Reviewed-by: Damien Neil <dneil@google.com>
37Run-TryBot: Roland Shoemaker <bracewell@google.com>
38Change-Id: If134890d75f0d95c681d67234daf191ba08e6424
39Reviewed-on: https://go-review.googlesource.com/c/go/+/481985
40Run-TryBot: Michael Knyszek <mknyszek@google.com>
41Auto-Submit: Michael Knyszek <mknyszek@google.com>
42TryBot-Result: Gopher Robot <gobot@golang.org>
43Reviewed-by: Matthew Dempsky <mdempsky@google.com>
44
45Upstream-Status: Backport [https://github.com/golang/go/commit/7917b5f31204528ea72e0629f0b7d52b35b27538]
46CVE: CVE-2023-24536
47Signed-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
56diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
57index 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,
113diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
114index 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
185diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go
186index 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 }
281diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go
282index 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)
291diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
292index 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--
3482.25.1
349