From f1b37a1aac836baf1fd658595b08abbfae4ba6b7 Mon Sep 17 00:00:00 2001 From: zhangzhihui Date: Wed, 10 May 2023 15:59:54 +0800 Subject: [PATCH] [Backport] fix some CVE CVE num upstream commit openEuler patch CVE-2023-29400 9db0e74f606b8afb28cc71d4b1c8b4ed24cabbf5 0016-release-branch.go1.19-html-template-emit-filterFails.patch CVE-2023-24540 ce7bd33345416e6d8cac901792060591cafc2797 0015-release-branch.go1.19-html-template-handle-all-JS-wh.patch CVE-2023-24539 e49282327b05192e46086bf25fd3ac691205fe80 0014-release-branch.go1.19-html-template-disallow-angle-b.patch CVE-2023-24538 b1e3ecfa06b67014429a197ec5e134ce4303ad9b 0013-release-branch.go1.19-html-template-disallow-actions.patch CVE-2023-24537 126a1d02da82f93ede7ce0bd8d3c51ef627f2104 0012-release-branch.go1.19-go-scanner-reject-large-line-a.patch CVE-2023-24536 7917b5f31204528ea72e0629f0b7d52b35b27538 0011-release-branch.go1.19-mime-multipart-limit-parsed-mi.patch CVE-2023-24536 7a359a651c7ebdb29e0a1c03102fce793e9f58f0 0010-release-branch.go1.19-net-textproto-mime-multipart-i.patch CVE-2023-24536 ef41a4e2face45e580c5836eaebd51629fc23f15 0009-release-branch.go1.19-mime-multipart-avoid-excessive.patch CVE-2023-24534 d6759e7a059f4208f07aa781402841d7ddaaef96 0008-release-branch.go1.19-net-textproto-avoid-overpredic.patch CVE-2023-24532 639b67ed114151c0d786aa26e7faeab942400703 0007-release-branch.go1.19-crypto-internal-nistec-reduce-.patch CVE-2022-41723 5c3e11bd0b5c0a86e5beffcd4339b86a902b21c3 0006-release-branch.go1.19-net-http-update-bundled-golang.patch CVE-2022-41724 00b256e9e3c0fa02a278ec9dfc3e191e02ceaf80 0005-release-branch.go1.19-crypto-tls-replace-all-usages-.patch CVE-2022-41725 5c55ac9bf1e5f779220294c843526536605f42ab 0004-release-branch.go1.19-mime-multipart-limit-memory-in.patch CVE-2022-41722 3345ddca41f00f9ed6fc3c1a36f6e2bede02d7ff 0003-release-branch.go1.19-path-filepath-do-not-Clean-a-..patch Signed-off-by: zhangzhihui --- ...o1.19-path-filepath-do-not-Clean-a-..patch | 134 + ...o1.19-mime-multipart-limit-memory-in.patch | 644 +++++ ...o1.19-crypto-tls-replace-all-usages-.patch | 2408 +++++++++++++++++ ...o1.19-net-http-update-bundled-golang.patch | 168 ++ ...o1.19-crypto-internal-nistec-reduce-.patch | 206 ++ ...o1.19-net-textproto-avoid-overpredic.patch | 2 +- ...o1.19-mime-multipart-avoid-excessive.patch | 133 + ...o1.19-net-textproto-mime-multipart-i.patch | 183 ++ ...o1.19-mime-multipart-limit-parsed-mi.patch | 344 +++ ...o1.19-go-scanner-reject-large-line-a.patch | 2 +- ...o1.19-html-template-disallow-actions.patch | 2 +- ...o1.19-html-template-disallow-angle-b.patch | 63 + ...o1.19-html-template-handle-all-JS-wh.patch | 89 + ...o1.19-html-template-emit-filterFails.patch | 95 + golang.spec | 32 +- 15 files changed, 4498 insertions(+), 7 deletions(-) create mode 100644 0003-release-branch.go1.19-path-filepath-do-not-Clean-a-..patch create mode 100644 0004-release-branch.go1.19-mime-multipart-limit-memory-in.patch create mode 100644 0005-release-branch.go1.19-crypto-tls-replace-all-usages-.patch create mode 100644 0006-release-branch.go1.19-net-http-update-bundled-golang.patch create mode 100644 0007-release-branch.go1.19-crypto-internal-nistec-reduce-.patch rename 0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch => 0008-release-branch.go1.19-net-textproto-avoid-overpredic.patch (99%) create mode 100644 0009-release-branch.go1.19-mime-multipart-avoid-excessive.patch create mode 100644 0010-release-branch.go1.19-net-textproto-mime-multipart-i.patch create mode 100644 0011-release-branch.go1.19-mime-multipart-limit-parsed-mi.patch rename 0003-release-branch.go1.19-go-scanner-reject-large-line.patch => 0012-release-branch.go1.19-go-scanner-reject-large-line-a.patch (99%) rename 0004-release-branch.go1.19-html-template-disallow-actions.patch => 0013-release-branch.go1.19-html-template-disallow-actions.patch (99%) create mode 100644 0014-release-branch.go1.19-html-template-disallow-angle-b.patch create mode 100644 0015-release-branch.go1.19-html-template-handle-all-JS-wh.patch create mode 100644 0016-release-branch.go1.19-html-template-emit-filterFails.patch diff --git a/0003-release-branch.go1.19-path-filepath-do-not-Clean-a-..patch b/0003-release-branch.go1.19-path-filepath-do-not-Clean-a-..patch new file mode 100644 index 0000000..f0b7fe1 --- /dev/null +++ b/0003-release-branch.go1.19-path-filepath-do-not-Clean-a-..patch @@ -0,0 +1,134 @@ +From 3345ddca41f00f9ed6fc3c1a36f6e2bede02d7ff Mon Sep 17 00:00:00 2001 +From: Damien Neil +Date: Mon, 12 Dec 2022 16:43:37 -0800 +Subject: [PATCH] [release-branch.go1.19] path/filepath: do not + Clean("a/../c:/b") into c:\b on Windows + +Do not permit Clean to convert a relative path into one starting +with a drive reference. This change causes Clean to insert a . +path element at the start of a path when the original path does not +start with a volume name, and the first path element would contain +a colon. + +This may introduce a spurious but harmless . path element under +some circumstances. For example, Clean("a/../b:/../c") becomes `.\c`. + +This reverts CL 401595, since the change here supersedes the one +in that CL. + +Thanks to RyotaK (https://twitter.com/ryotkak) for reporting this issue. + +Updates #57274 +Fixes #57275 +Fixes CVE-2022-41722 + +Change-Id: I837446285a03aa74c79d7642720e01f354c2ca17 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1675249 +Reviewed-by: Roland Shoemaker +Run-TryBot: Damien Neil +Reviewed-by: Julie Qiu +TryBot-Result: Security TryBots +(cherry picked from commit 780dfa043ff5192c37de0d6fd1053a66b2b9f378) +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728206 +Reviewed-by: Damien Neil +Run-TryBot: Roland Shoemaker +Reviewed-by: Tatiana Bradley +Reviewed-on: https://go-review.googlesource.com/c/go/+/468115 +Reviewed-by: Than McIntosh +Run-TryBot: Michael Pratt +Auto-Submit: Michael Pratt +TryBot-Bypass: Michael Pratt +--- + src/path/filepath/path.go | 27 +++++++++++++------------- + src/path/filepath/path_test.go | 7 +++++++ + src/path/filepath/path_windows_test.go | 2 +- + 3 files changed, 22 insertions(+), 14 deletions(-) + +diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go +index de7a2c758b..9b1f5ed7c0 100644 +--- a/src/path/filepath/path.go ++++ b/src/path/filepath/path.go +@@ -15,6 +15,7 @@ import ( + "errors" + "io/fs" + "os" ++ "runtime" + "sort" + "strings" + ) +@@ -117,21 +118,9 @@ func Clean(path string) string { + case os.IsPathSeparator(path[r]): + // empty path element + r++ +- case path[r] == '.' && r+1 == n: ++ case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): + // . element + r++ +- case path[r] == '.' && os.IsPathSeparator(path[r+1]): +- // ./ element +- r++ +- +- for r < len(path) && os.IsPathSeparator(path[r]) { +- r++ +- } +- if out.w == 0 && volumeNameLen(path[r:]) > 0 { +- // When joining prefix "." and an absolute path on Windows, +- // the prefix should not be removed. +- out.append('.') +- } + case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): + // .. element: remove to last separator + r += 2 +@@ -157,6 +146,18 @@ func Clean(path string) string { + if rooted && out.w != 1 || !rooted && out.w != 0 { + out.append(Separator) + } ++ // If a ':' appears in the path element at the start of a Windows path, ++ // insert a .\ at the beginning to avoid converting relative paths ++ // like a/../c: into c:. ++ if runtime.GOOS == "windows" && out.w == 0 && out.volLen == 0 && r != 0 { ++ for i := r; i < n && !os.IsPathSeparator(path[i]); i++ { ++ if path[i] == ':' { ++ out.append('.') ++ out.append(Separator) ++ break ++ } ++ } ++ } + // copy element + for ; r < n && !os.IsPathSeparator(path[r]); r++ { + out.append(path[r]) +diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go +index a783d6be28..9a57920dd7 100644 +--- a/src/path/filepath/path_test.go ++++ b/src/path/filepath/path_test.go +@@ -96,6 +96,13 @@ var wincleantests = []PathTest{ + {`.\c:`, `.\c:`}, + {`.\c:\foo`, `.\c:\foo`}, + {`.\c:foo`, `.\c:foo`}, ++ ++ // Don't allow cleaning to move an element with a colon to the start of the path. ++ {`a/../c:`, `.\c:`}, ++ {`a\..\c:`, `.\c:`}, ++ {`a/../c:/a`, `.\c:\a`}, ++ {`a/../../c:`, `..\c:`}, ++ {`foo:bar`, `foo:bar`}, + } + + func TestClean(t *testing.T) { +diff --git a/src/path/filepath/path_windows_test.go b/src/path/filepath/path_windows_test.go +index 9e6c0ec81d..857f4d5c7c 100644 +--- a/src/path/filepath/path_windows_test.go ++++ b/src/path/filepath/path_windows_test.go +@@ -542,7 +542,7 @@ func TestIssue52476(t *testing.T) { + }{ + {`..\.`, `C:`, `..\C:`}, + {`..`, `C:`, `..\C:`}, +- {`.`, `:`, `:`}, ++ {`.`, `:`, `.\:`}, + {`.`, `C:`, `.\C:`}, + {`.`, `C:/a/b/../c`, `.\C:\a\c`}, + {`.`, `\C:`, `.\C:`}, +-- +2.37.1 + diff --git a/0004-release-branch.go1.19-mime-multipart-limit-memory-in.patch b/0004-release-branch.go1.19-mime-multipart-limit-memory-in.patch new file mode 100644 index 0000000..70803b7 --- /dev/null +++ b/0004-release-branch.go1.19-mime-multipart-limit-memory-in.patch @@ -0,0 +1,644 @@ +From 5c55ac9bf1e5f779220294c843526536605f42ab Mon Sep 17 00:00:00 2001 +From: Damien Neil +Date: Wed, 25 Jan 2023 09:27:01 -0800 +Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit memory/inode + consumption of ReadForm + +Reader.ReadForm is documented as storing "up to maxMemory bytes + 10MB" +in memory. Parsed forms can consume substantially more memory than +this limit, since ReadForm does not account for map entry overhead +and MIME headers. + +In addition, while the amount of disk memory consumed by ReadForm can +be constrained by limiting the size of the parsed input, ReadForm will +create one temporary file per form part stored on disk, potentially +consuming a large number of inodes. + +Update ReadForm's memory accounting to include part names, +MIME headers, and map entry overhead. + +Update ReadForm to store all on-disk file parts in a single +temporary file. + +Files returned by FileHeader.Open are documented as having a concrete +type of *os.File when a file is stored on disk. The change to use a +single temporary file for all parts means that this is no longer the +case when a form contains more than a single file part stored on disk. + +The previous behavior of storing each file part in a separate disk +file may be reenabled with GODEBUG=multipartfiles=distinct. + +Update Reader.NextPart and Reader.NextRawPart to set a 10MiB cap +on the size of MIME headers. + +Thanks to Jakob Ackermann (@das7pad) for reporting this issue. + +Updates #58006 +Fixes #58362 +Fixes CVE-2022-41725 + +Change-Id: Ibd780a6c4c83ac8bcfd3cbe344f042e9940f2eab +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1714276 +Reviewed-by: Julie Qiu +TryBot-Result: Security TryBots +Reviewed-by: Roland Shoemaker +Run-TryBot: Damien Neil +(cherry picked from commit ed4664330edcd91b24914c9371c377c132dbce8c) +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728949 +Reviewed-by: Tatiana Bradley +Run-TryBot: Roland Shoemaker +Reviewed-by: Damien Neil +Reviewed-on: https://go-review.googlesource.com/c/go/+/468116 +TryBot-Result: Gopher Robot +Reviewed-by: Than McIntosh +Run-TryBot: Michael Pratt +Auto-Submit: Michael Pratt +--- + src/mime/multipart/formdata.go | 132 ++++++++++++++++++++----- + src/mime/multipart/formdata_test.go | 140 ++++++++++++++++++++++++++- + src/mime/multipart/multipart.go | 25 +++-- + src/mime/multipart/readmimeheader.go | 14 +++ + src/net/http/request_test.go | 2 +- + src/net/textproto/reader.go | 20 +++- + 6 files changed, 295 insertions(+), 38 deletions(-) + create mode 100644 src/mime/multipart/readmimeheader.go + +diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go +index fca5f9e15f..a7d4ca97f0 100644 +--- a/src/mime/multipart/formdata.go ++++ b/src/mime/multipart/formdata.go +@@ -7,6 +7,7 @@ package multipart + import ( + "bytes" + "errors" ++ "internal/godebug" + "io" + "math" + "net/textproto" +@@ -33,23 +34,58 @@ func (r *Reader) ReadForm(maxMemory int64) (*Form, error) { + + func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + form := &Form{make(map[string][]string), make(map[string][]*FileHeader)} ++ var ( ++ file *os.File ++ fileOff int64 ++ ) ++ numDiskFiles := 0 ++ multipartFiles := godebug.Get("multipartfiles") ++ combineFiles := multipartFiles != "distinct" + defer func() { ++ if file != nil { ++ if cerr := file.Close(); err == nil { ++ err = cerr ++ } ++ } ++ if combineFiles && numDiskFiles > 1 { ++ for _, fhs := range form.File { ++ for _, fh := range fhs { ++ fh.tmpshared = true ++ } ++ } ++ } + if err != nil { + form.RemoveAll() ++ if file != nil { ++ os.Remove(file.Name()) ++ } + } + }() + +- // Reserve an additional 10 MB for non-file parts. +- maxValueBytes := maxMemory + int64(10<<20) +- if maxValueBytes <= 0 { ++ // maxFileMemoryBytes is the maximum bytes of file data we will store in memory. ++ // Data past this limit is written to disk. ++ // This limit strictly applies to content, not metadata (filenames, MIME headers, etc.), ++ // since metadata is always stored in memory, not disk. ++ // ++ // maxMemoryBytes is the maximum bytes we will store in memory, including file content, ++ // non-file part values, metdata, and map entry overhead. ++ // ++ // We reserve an additional 10 MB in maxMemoryBytes for non-file data. ++ // ++ // The relationship between these parameters, as well as the overly-large and ++ // unconfigurable 10 MB added on to maxMemory, is unfortunate but difficult to change ++ // within the constraints of the API as documented. ++ maxFileMemoryBytes := maxMemory ++ maxMemoryBytes := maxMemory + int64(10<<20) ++ if maxMemoryBytes <= 0 { + if maxMemory < 0 { +- maxValueBytes = 0 ++ maxMemoryBytes = 0 + } else { +- maxValueBytes = math.MaxInt64 ++ maxMemoryBytes = math.MaxInt64 + } + } + for { +- p, err := r.NextPart() ++ p, err := r.nextPart(false, maxMemoryBytes) + if err == io.EOF { + break + } +@@ -63,16 +99,27 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + } + filename := p.FileName() + ++ // Multiple values for the same key (one map entry, longer slice) are cheaper ++ // than the same number of values for different keys (many map entries), but ++ // using a consistent per-value cost for overhead is simpler. ++ maxMemoryBytes -= int64(len(name)) ++ maxMemoryBytes -= 100 // map overhead ++ if maxMemoryBytes < 0 { ++ // We can't actually take this path, since nextPart would already have ++ // rejected the MIME headers for being too large. Check anyway. ++ return nil, ErrMessageTooLarge ++ } ++ + var b bytes.Buffer + + if filename == "" { + // value, store as string in memory +- n, err := io.CopyN(&b, p, maxValueBytes+1) ++ n, err := io.CopyN(&b, p, maxMemoryBytes+1) + if err != nil && err != io.EOF { + return nil, err + } +- maxValueBytes -= n +- if maxValueBytes < 0 { ++ maxMemoryBytes -= n ++ if maxMemoryBytes < 0 { + return nil, ErrMessageTooLarge + } + form.Value[name] = append(form.Value[name], b.String()) +@@ -80,35 +127,45 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + } + + // file, store in memory or on disk ++ maxMemoryBytes -= mimeHeaderSize(p.Header) ++ if maxMemoryBytes < 0 { ++ return nil, ErrMessageTooLarge ++ } + fh := &FileHeader{ + Filename: filename, + Header: p.Header, + } +- n, err := io.CopyN(&b, p, maxMemory+1) ++ n, err := io.CopyN(&b, p, maxFileMemoryBytes+1) + if err != nil && err != io.EOF { + return nil, err + } +- if n > maxMemory { +- // too big, write to disk and flush buffer +- file, err := os.CreateTemp("", "multipart-") +- if err != nil { +- return nil, err ++ if n > maxFileMemoryBytes { ++ if file == nil { ++ file, err = os.CreateTemp(r.tempDir, "multipart-") ++ if err != nil { ++ return nil, err ++ } + } ++ numDiskFiles++ + size, err := io.Copy(file, io.MultiReader(&b, p)) +- if cerr := file.Close(); err == nil { +- err = cerr +- } + if err != nil { +- os.Remove(file.Name()) + return nil, err + } + fh.tmpfile = file.Name() + fh.Size = size ++ fh.tmpoff = fileOff ++ fileOff += size ++ if !combineFiles { ++ if err := file.Close(); err != nil { ++ return nil, err ++ } ++ file = nil ++ } + } else { + fh.content = b.Bytes() + fh.Size = int64(len(fh.content)) +- maxMemory -= n +- maxValueBytes -= n ++ maxFileMemoryBytes -= n ++ maxMemoryBytes -= n + } + form.File[name] = append(form.File[name], fh) + } +@@ -116,6 +173,17 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + return form, nil + } + ++func mimeHeaderSize(h textproto.MIMEHeader) (size int64) { ++ for k, vs := range h { ++ size += int64(len(k)) ++ size += 100 // map entry overhead ++ for _, v := range vs { ++ size += int64(len(v)) ++ } ++ } ++ return size ++} ++ + // Form is a parsed multipart form. + // Its File parts are stored either in memory or on disk, + // and are accessible via the *FileHeader's Open method. +@@ -133,7 +201,7 @@ func (f *Form) RemoveAll() error { + for _, fh := range fhs { + if fh.tmpfile != "" { + e := os.Remove(fh.tmpfile) +- if e != nil && err == nil { ++ if e != nil && !errors.Is(e, os.ErrNotExist) && err == nil { + err = e + } + } +@@ -148,15 +216,25 @@ type FileHeader struct { + Header textproto.MIMEHeader + Size int64 + +- content []byte +- tmpfile string ++ content []byte ++ tmpfile string ++ tmpoff int64 ++ tmpshared bool + } + + // Open opens and returns the FileHeader's associated File. + func (fh *FileHeader) Open() (File, error) { + if b := fh.content; b != nil { + r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))) +- return sectionReadCloser{r}, nil ++ return sectionReadCloser{r, nil}, nil ++ } ++ if fh.tmpshared { ++ f, err := os.Open(fh.tmpfile) ++ if err != nil { ++ return nil, err ++ } ++ r := io.NewSectionReader(f, fh.tmpoff, fh.Size) ++ return sectionReadCloser{r, f}, nil + } + return os.Open(fh.tmpfile) + } +@@ -175,8 +253,12 @@ type File interface { + + type sectionReadCloser struct { + *io.SectionReader ++ io.Closer + } + + func (rc sectionReadCloser) Close() error { ++ if rc.Closer != nil { ++ return rc.Closer.Close() ++ } + return nil + } +diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go +index e3a3a3eae8..5cded7170c 100644 +--- a/src/mime/multipart/formdata_test.go ++++ b/src/mime/multipart/formdata_test.go +@@ -6,8 +6,10 @@ package multipart + + import ( + "bytes" ++ "fmt" + "io" + "math" ++ "net/textproto" + "os" + "strings" + "testing" +@@ -208,8 +210,8 @@ Content-Disposition: form-data; name="largetext" + maxMemory int64 + err error + }{ +- {"smaller", 50, nil}, +- {"exact-fit", 25, nil}, ++ {"smaller", 50 + int64(len("largetext")) + 100, nil}, ++ {"exact-fit", 25 + int64(len("largetext")) + 100, nil}, + {"too-large", 0, ErrMessageTooLarge}, + } + for _, tc := range testCases { +@@ -224,7 +226,7 @@ Content-Disposition: form-data; name="largetext" + defer f.RemoveAll() + } + if tc.err != err { +- t.Fatalf("ReadForm error - got: %v; expected: %v", tc.err, err) ++ t.Fatalf("ReadForm error - got: %v; expected: %v", err, tc.err) + } + if err == nil { + if g := f.Value["largetext"][0]; g != largeTextValue { +@@ -234,3 +236,135 @@ Content-Disposition: form-data; name="largetext" + }) + } + } ++ ++// TestReadForm_MetadataTooLarge verifies that we account for the size of field names, ++// MIME headers, and map entry overhead while limiting the memory consumption of parsed forms. ++func TestReadForm_MetadataTooLarge(t *testing.T) { ++ for _, test := range []struct { ++ name string ++ f func(*Writer) ++ }{{ ++ name: "large name", ++ f: func(fw *Writer) { ++ name := strings.Repeat("a", 10<<20) ++ w, _ := fw.CreateFormField(name) ++ w.Write([]byte("value")) ++ }, ++ }, { ++ name: "large MIME header", ++ f: func(fw *Writer) { ++ h := make(textproto.MIMEHeader) ++ h.Set("Content-Disposition", `form-data; name="a"`) ++ h.Set("X-Foo", strings.Repeat("a", 10<<20)) ++ w, _ := fw.CreatePart(h) ++ w.Write([]byte("value")) ++ }, ++ }, { ++ name: "many parts", ++ f: func(fw *Writer) { ++ for i := 0; i < 110000; i++ { ++ w, _ := fw.CreateFormField("f") ++ w.Write([]byte("v")) ++ } ++ }, ++ }} { ++ t.Run(test.name, func(t *testing.T) { ++ var buf bytes.Buffer ++ fw := NewWriter(&buf) ++ test.f(fw) ++ if err := fw.Close(); err != nil { ++ t.Fatal(err) ++ } ++ fr := NewReader(&buf, fw.Boundary()) ++ _, err := fr.ReadForm(0) ++ if err != ErrMessageTooLarge { ++ t.Errorf("fr.ReadForm() = %v, want ErrMessageTooLarge", err) ++ } ++ }) ++ } ++} ++ ++// TestReadForm_ManyFiles_Combined tests that a multipart form containing many files only ++// results in a single on-disk file. ++func TestReadForm_ManyFiles_Combined(t *testing.T) { ++ const distinct = false ++ testReadFormManyFiles(t, distinct) ++} ++ ++// TestReadForm_ManyFiles_Distinct tests that setting GODEBUG=multipartfiles=distinct ++// results in every file in a multipart form being placed in a distinct on-disk file. ++func TestReadForm_ManyFiles_Distinct(t *testing.T) { ++ t.Setenv("GODEBUG", "multipartfiles=distinct") ++ const distinct = true ++ testReadFormManyFiles(t, distinct) ++} ++ ++func testReadFormManyFiles(t *testing.T, distinct bool) { ++ var buf bytes.Buffer ++ fw := NewWriter(&buf) ++ const numFiles = 10 ++ for i := 0; i < numFiles; i++ { ++ name := fmt.Sprint(i) ++ w, err := fw.CreateFormFile(name, name) ++ if err != nil { ++ t.Fatal(err) ++ } ++ w.Write([]byte(name)) ++ } ++ if err := fw.Close(); err != nil { ++ t.Fatal(err) ++ } ++ fr := NewReader(&buf, fw.Boundary()) ++ fr.tempDir = t.TempDir() ++ form, err := fr.ReadForm(0) ++ if err != nil { ++ t.Fatal(err) ++ } ++ for i := 0; i < numFiles; i++ { ++ name := fmt.Sprint(i) ++ if got := len(form.File[name]); got != 1 { ++ t.Fatalf("form.File[%q] has %v entries, want 1", name, got) ++ } ++ fh := form.File[name][0] ++ file, err := fh.Open() ++ if err != nil { ++ t.Fatalf("form.File[%q].Open() = %v", name, err) ++ } ++ if distinct { ++ if _, ok := file.(*os.File); !ok { ++ t.Fatalf("form.File[%q].Open: %T, want *os.File", name, file) ++ } ++ } ++ got, err := io.ReadAll(file) ++ file.Close() ++ if string(got) != name || err != nil { ++ t.Fatalf("read form.File[%q]: %q, %v; want %q, nil", name, string(got), err, name) ++ } ++ } ++ dir, err := os.Open(fr.tempDir) ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer dir.Close() ++ names, err := dir.Readdirnames(0) ++ if err != nil { ++ t.Fatal(err) ++ } ++ wantNames := 1 ++ if distinct { ++ wantNames = numFiles ++ } ++ if len(names) != wantNames { ++ t.Fatalf("temp dir contains %v files; want 1", len(names)) ++ } ++ if err := form.RemoveAll(); err != nil { ++ t.Fatalf("form.RemoveAll() = %v", err) ++ } ++ names, err = dir.Readdirnames(0) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if len(names) != 0 { ++ t.Fatalf("temp dir contains %v files; want 0", len(names)) ++ } ++} +diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go +index aa05ac8f9c..fc50d35196 100644 +--- a/src/mime/multipart/multipart.go ++++ b/src/mime/multipart/multipart.go +@@ -128,12 +128,12 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) { + return n, r.err + } + +-func newPart(mr *Reader, rawPart bool) (*Part, error) { ++func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { + bp := &Part{ + Header: make(map[string][]string), + mr: mr, + } +- if err := bp.populateHeaders(); err != nil { ++ if err := bp.populateHeaders(maxMIMEHeaderSize); err != nil { + return nil, err + } + bp.r = partReader{bp} +@@ -149,12 +149,16 @@ func newPart(mr *Reader, rawPart bool) (*Part, error) { + return bp, nil + } + +-func (p *Part) populateHeaders() error { ++func (p *Part) populateHeaders(maxMIMEHeaderSize int64) error { + r := textproto.NewReader(p.mr.bufReader) +- header, err := r.ReadMIMEHeader() ++ header, err := readMIMEHeader(r, maxMIMEHeaderSize) + if err == nil { + p.Header = header + } ++ // TODO: Add a distinguishable error to net/textproto. ++ if err != nil && err.Error() == "message too large" { ++ err = ErrMessageTooLarge ++ } + return err + } + +@@ -311,6 +315,7 @@ func (p *Part) Close() error { + // isn't supported. + type Reader struct { + bufReader *bufio.Reader ++ tempDir string // used in tests + + currentPart *Part + partsRead int +@@ -321,6 +326,10 @@ type Reader struct { + dashBoundary []byte // "--boundary" + } + ++// maxMIMEHeaderSize is the maximum size of a MIME header we will parse, ++// including header keys, values, and map overhead. ++const maxMIMEHeaderSize = 10 << 20 ++ + // NextPart returns the next part in the multipart or an error. + // When there are no more parts, the error io.EOF is returned. + // +@@ -328,7 +337,7 @@ type Reader struct { + // has a value of "quoted-printable", that header is instead + // hidden and the body is transparently decoded during Read calls. + func (r *Reader) NextPart() (*Part, error) { +- return r.nextPart(false) ++ return r.nextPart(false, maxMIMEHeaderSize) + } + + // NextRawPart returns the next part in the multipart or an error. +@@ -337,10 +346,10 @@ func (r *Reader) NextPart() (*Part, error) { + // Unlike NextPart, it does not have special handling for + // "Content-Transfer-Encoding: quoted-printable". + func (r *Reader) NextRawPart() (*Part, error) { +- return r.nextPart(true) ++ return r.nextPart(true, maxMIMEHeaderSize) + } + +-func (r *Reader) nextPart(rawPart bool) (*Part, error) { ++func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { + if r.currentPart != nil { + r.currentPart.Close() + } +@@ -365,7 +374,7 @@ func (r *Reader) nextPart(rawPart bool) (*Part, error) { + + if r.isBoundaryDelimiterLine(line) { + r.partsRead++ +- bp, err := newPart(r, rawPart) ++ bp, err := newPart(r, rawPart, maxMIMEHeaderSize) + if err != nil { + return nil, err + } +diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go +new file mode 100644 +index 0000000000..6836928c9e +--- /dev/null ++++ b/src/mime/multipart/readmimeheader.go +@@ -0,0 +1,14 @@ ++// Copyright 2023 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++package multipart ++ ++import ( ++ "net/textproto" ++ _ "unsafe" // for go:linkname ++) ++ ++// readMIMEHeader is defined in package net/textproto. ++// ++//go:linkname readMIMEHeader net/textproto.readMIMEHeader ++func readMIMEHeader(r *textproto.Reader, lim int64) (textproto.MIMEHeader, error) +diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go +index af35f17f7c..0ec8f2474a 100644 +--- a/src/net/http/request_test.go ++++ b/src/net/http/request_test.go +@@ -1104,7 +1104,7 @@ func testMissingFile(t *testing.T, req *Request) { + t.Errorf("FormFile file = %v, want nil", f) + } + if fh != nil { +- t.Errorf("FormFile file header = %q, want nil", fh) ++ t.Errorf("FormFile file header = %v, want nil", fh) + } + if err != ErrMissingFile { + t.Errorf("FormFile err = %q, want ErrMissingFile", err) +diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go +index 1f7afc5766..b37be54d67 100644 +--- a/src/net/textproto/reader.go ++++ b/src/net/textproto/reader.go +@@ -7,8 +7,10 @@ package textproto + import ( + "bufio" + "bytes" ++ "errors" + "fmt" + "io" ++ "math" + "strconv" + "strings" + "sync" +@@ -481,6 +483,12 @@ var colon = []byte(":") + // "Long-Key": {"Even Longer Value"}, + // } + func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { ++ return readMIMEHeader(r, math.MaxInt64) ++} ++ ++// readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size. ++// It is called by the mime/multipart package. ++func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { + // Avoid lots of small slice allocations later by allocating one + // large one ahead of time which we'll cut up into smaller + // slices. If this isn't big enough later, we allocate small ones. +@@ -522,9 +530,19 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { + } + + // Skip initial spaces in value. +- value := strings.TrimLeft(string(v), " \t") ++ value := string(bytes.TrimLeft(v, " \t")) + + vv := m[key] ++ if vv == nil { ++ lim -= int64(len(key)) ++ lim -= 100 // map entry overhead ++ } ++ lim -= int64(len(value)) ++ if lim < 0 { ++ // TODO: This should be a distinguishable error (ErrMessageTooLarge) ++ // to allow mime/multipart to detect it. ++ return m, errors.New("message too large") ++ } + if vv == nil && len(strs) > 0 { + // More than likely this will be a single-element key. + // Most headers aren't multi-valued. +-- +2.37.1 + diff --git a/0005-release-branch.go1.19-crypto-tls-replace-all-usages-.patch b/0005-release-branch.go1.19-crypto-tls-replace-all-usages-.patch new file mode 100644 index 0000000..03d46ec --- /dev/null +++ b/0005-release-branch.go1.19-crypto-tls-replace-all-usages-.patch @@ -0,0 +1,2408 @@ +From 00b256e9e3c0fa02a278ec9dfc3e191e02ceaf80 Mon Sep 17 00:00:00 2001 +From: Roland Shoemaker +Date: Wed, 14 Dec 2022 09:43:16 -0800 +Subject: [PATCH] [release-branch.go1.19] crypto/tls: replace all usages of + BytesOrPanic + +Message marshalling makes use of BytesOrPanic a lot, under the +assumption that it will never panic. This assumption was incorrect, and +specifically crafted handshakes could trigger panics. Rather than just +surgically replacing the usages of BytesOrPanic in paths that could +panic, replace all usages of it with proper error returns in case there +are other ways of triggering panics which we didn't find. + +In one specific case, the tree routed by expandLabel, we replace the +usage of BytesOrPanic, but retain a panic. This function already +explicitly panicked elsewhere, and returning an error from it becomes +rather painful because it requires changing a large number of APIs. +The marshalling is unlikely to ever panic, as the inputs are all either +fixed length, or already limited to the sizes required. If it were to +panic, it'd likely only be during development. A close inspection shows +no paths for a user to cause a panic currently. + +This patches ends up being rather large, since it requires routing +errors back through functions which previously had no error returns. +Where possible I've tried to use helpers that reduce the verbosity +of frequently repeated stanzas, and to make the diffs as minimal as +possible. + +Thanks to Marten Seemann for reporting this issue. + +Updates #58001 +Fixes #58358 +Fixes CVE-2022-41724 + +Change-Id: Ieb55867ef0a3e1e867b33f09421932510cb58851 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1679436 +Reviewed-by: Julie Qiu +TryBot-Result: Security TryBots +Run-TryBot: Roland Shoemaker +Reviewed-by: Damien Neil +(cherry picked from commit 0f3a44ad7b41cc89efdfad25278953e17d9c1e04) +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728204 +Reviewed-by: Tatiana Bradley +Reviewed-on: https://go-review.googlesource.com/c/go/+/468117 +Auto-Submit: Michael Pratt +Run-TryBot: Michael Pratt +TryBot-Result: Gopher Robot +Reviewed-by: Than McIntosh +--- + src/crypto/tls/boring_test.go | 2 +- + src/crypto/tls/common.go | 2 +- + src/crypto/tls/conn.go | 46 +- + src/crypto/tls/handshake_client.go | 95 +-- + src/crypto/tls/handshake_client_test.go | 4 +- + src/crypto/tls/handshake_client_tls13.go | 74 ++- + src/crypto/tls/handshake_messages.go | 716 +++++++++++----------- + src/crypto/tls/handshake_messages_test.go | 19 +- + src/crypto/tls/handshake_server.go | 73 ++- + src/crypto/tls/handshake_server_test.go | 31 +- + src/crypto/tls/handshake_server_tls13.go | 71 ++- + src/crypto/tls/key_schedule.go | 19 +- + src/crypto/tls/ticket.go | 8 +- + 13 files changed, 657 insertions(+), 503 deletions(-) + +diff --git a/src/crypto/tls/boring_test.go b/src/crypto/tls/boring_test.go +index 59d4d2b272..ba68f355eb 100644 +--- a/src/crypto/tls/boring_test.go ++++ b/src/crypto/tls/boring_test.go +@@ -269,7 +269,7 @@ func TestBoringClientHello(t *testing.T) { + + go Client(c, clientConfig).Handshake() + srv := Server(s, testConfig) +- msg, err := srv.readHandshake() ++ msg, err := srv.readHandshake(nil) + if err != nil { + t.Fatal(err) + } +diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go +index 1861efce66..14427cc112 100644 +--- a/src/crypto/tls/common.go ++++ b/src/crypto/tls/common.go +@@ -1384,7 +1384,7 @@ func (c *Certificate) leaf() (*x509.Certificate, error) { + } + + type handshakeMessage interface { +- marshal() []byte ++ marshal() ([]byte, error) + unmarshal([]byte) bool + } + +diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go +index 1861a312f1..73f0c1771e 100644 +--- a/src/crypto/tls/conn.go ++++ b/src/crypto/tls/conn.go +@@ -1003,18 +1003,37 @@ func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { + return n, nil + } + +-// writeRecord writes a TLS record with the given type and payload to the +-// connection and updates the record layer state. +-func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) { ++// writeHandshakeRecord writes a handshake message to the connection and updates ++// the record layer state. If transcript is non-nil the marshalled message is ++// written to it. ++func (c *Conn) writeHandshakeRecord(msg handshakeMessage, transcript transcriptHash) (int, error) { + c.out.Lock() + defer c.out.Unlock() + +- return c.writeRecordLocked(typ, data) ++ data, err := msg.marshal() ++ if err != nil { ++ return 0, err ++ } ++ if transcript != nil { ++ transcript.Write(data) ++ } ++ ++ return c.writeRecordLocked(recordTypeHandshake, data) ++} ++ ++// writeChangeCipherRecord writes a ChangeCipherSpec message to the connection and ++// updates the record layer state. ++func (c *Conn) writeChangeCipherRecord() error { ++ c.out.Lock() ++ defer c.out.Unlock() ++ _, err := c.writeRecordLocked(recordTypeChangeCipherSpec, []byte{1}) ++ return err + } + + // readHandshake reads the next handshake message from +-// the record layer. +-func (c *Conn) readHandshake() (any, error) { ++// the record layer. If transcript is non-nil, the message ++// is written to the passed transcriptHash. ++func (c *Conn) readHandshake(transcript transcriptHash) (any, error) { + for c.hand.Len() < 4 { + if err := c.readRecord(); err != nil { + return nil, err +@@ -1093,6 +1112,11 @@ func (c *Conn) readHandshake() (any, error) { + if !m.unmarshal(data) { + return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } ++ ++ if transcript != nil { ++ transcript.Write(data) ++ } ++ + return m, nil + } + +@@ -1168,7 +1192,7 @@ func (c *Conn) handleRenegotiation() error { + return errors.New("tls: internal error: unexpected renegotiation") + } + +- msg, err := c.readHandshake() ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +@@ -1214,7 +1238,7 @@ func (c *Conn) handlePostHandshakeMessage() error { + return c.handleRenegotiation() + } + +- msg, err := c.readHandshake() ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +@@ -1250,7 +1274,11 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error { + defer c.out.Unlock() + + msg := &keyUpdateMsg{} +- _, err := c.writeRecordLocked(recordTypeHandshake, msg.marshal()) ++ msgBytes, err := msg.marshal() ++ if err != nil { ++ return err ++ } ++ _, err = c.writeRecordLocked(recordTypeHandshake, msgBytes) + if err != nil { + // Surface the error at the next write. + c.out.setErrorLocked(err) +diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go +index e61e3eb540..898d2e9af6 100644 +--- a/src/crypto/tls/handshake_client.go ++++ b/src/crypto/tls/handshake_client.go +@@ -162,7 +162,10 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { + } + c.serverName = hello.serverName + +- cacheKey, session, earlySecret, binderKey := c.loadSession(hello) ++ cacheKey, session, earlySecret, binderKey, err := c.loadSession(hello) ++ if err != nil { ++ return err ++ } + if cacheKey != "" && session != nil { + defer func() { + // If we got a handshake failure when resuming a session, throw away +@@ -177,11 +180,12 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { + }() + } + +- if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil { ++ if _, err := c.writeHandshakeRecord(hello, nil); err != nil { + return err + } + +- msg, err := c.readHandshake() ++ // serverHelloMsg is not included in the transcript ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +@@ -246,9 +250,9 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { + } + + func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, +- session *ClientSessionState, earlySecret, binderKey []byte) { ++ session *ClientSessionState, earlySecret, binderKey []byte, err error) { + if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { +- return "", nil, nil, nil ++ return "", nil, nil, nil, nil + } + + hello.ticketSupported = true +@@ -263,14 +267,14 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + // renegotiation is primarily used to allow a client to send a client + // certificate, which would be skipped if session resumption occurred. + if c.handshakes != 0 { +- return "", nil, nil, nil ++ return "", nil, nil, nil, nil + } + + // Try to resume a previously negotiated TLS session, if available. + cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + session, ok := c.config.ClientSessionCache.Get(cacheKey) + if !ok || session == nil { +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + + // Check that version used for the previous session is still valid. +@@ -282,7 +286,7 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + } + } + if !versOk { +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + + // Check that the cached server certificate is not expired, and that it's +@@ -291,16 +295,16 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + if !c.config.InsecureSkipVerify { + if len(session.verifiedChains) == 0 { + // The original connection had InsecureSkipVerify, while this doesn't. +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + serverCert := session.serverCertificates[0] + if c.config.time().After(serverCert.NotAfter) { + // Expired certificate, delete the entry. + c.config.ClientSessionCache.Put(cacheKey, nil) +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + if err := serverCert.VerifyHostname(c.config.ServerName); err != nil { +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + } + +@@ -308,7 +312,7 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + // In TLS 1.2 the cipher suite must match the resumed session. Ensure we + // are still offering it. + if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil { +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + + hello.sessionTicket = session.sessionTicket +@@ -318,14 +322,14 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + // Check that the session ticket is not expired. + if c.config.time().After(session.useBy) { + c.config.ClientSessionCache.Put(cacheKey, nil) +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + + // In TLS 1.3 the KDF hash must match the resumed session. Ensure we + // offer at least one cipher suite with that hash. + cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite) + if cipherSuite == nil { +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + cipherSuiteOk := false + for _, offeredID := range hello.cipherSuites { +@@ -336,7 +340,7 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + } + } + if !cipherSuiteOk { +- return cacheKey, nil, nil, nil ++ return cacheKey, nil, nil, nil, nil + } + + // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1. +@@ -354,9 +358,15 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + earlySecret = cipherSuite.extract(psk, nil) + binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil) + transcript := cipherSuite.hash.New() +- transcript.Write(hello.marshalWithoutBinders()) ++ helloBytes, err := hello.marshalWithoutBinders() ++ if err != nil { ++ return "", nil, nil, nil, err ++ } ++ transcript.Write(helloBytes) + pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)} +- hello.updateBinders(pskBinders) ++ if err := hello.updateBinders(pskBinders); err != nil { ++ return "", nil, nil, nil, err ++ } + + return + } +@@ -401,8 +411,12 @@ func (hs *clientHandshakeState) handshake() error { + hs.finishedHash.discardHandshakeBuffer() + } + +- hs.finishedHash.Write(hs.hello.marshal()) +- hs.finishedHash.Write(hs.serverHello.marshal()) ++ if err := transcriptMsg(hs.hello, &hs.finishedHash); err != nil { ++ return err ++ } ++ if err := transcriptMsg(hs.serverHello, &hs.finishedHash); err != nil { ++ return err ++ } + + c.buffering = true + c.didResume = isResume +@@ -473,7 +487,7 @@ func (hs *clientHandshakeState) pickCipherSuite() error { + func (hs *clientHandshakeState) doFullHandshake() error { + c := hs.c + +- msg, err := c.readHandshake() ++ msg, err := c.readHandshake(&hs.finishedHash) + if err != nil { + return err + } +@@ -482,9 +496,8 @@ func (hs *clientHandshakeState) doFullHandshake() error { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } +- hs.finishedHash.Write(certMsg.marshal()) + +- msg, err = c.readHandshake() ++ msg, err = c.readHandshake(&hs.finishedHash) + if err != nil { + return err + } +@@ -502,11 +515,10 @@ func (hs *clientHandshakeState) doFullHandshake() error { + c.sendAlert(alertUnexpectedMessage) + return errors.New("tls: received unexpected CertificateStatus message") + } +- hs.finishedHash.Write(cs.marshal()) + + c.ocspResponse = cs.response + +- msg, err = c.readHandshake() ++ msg, err = c.readHandshake(&hs.finishedHash) + if err != nil { + return err + } +@@ -535,14 +547,13 @@ func (hs *clientHandshakeState) doFullHandshake() error { + + skx, ok := msg.(*serverKeyExchangeMsg) + if ok { +- hs.finishedHash.Write(skx.marshal()) + err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx) + if err != nil { + c.sendAlert(alertUnexpectedMessage) + return err + } + +- msg, err = c.readHandshake() ++ msg, err = c.readHandshake(&hs.finishedHash) + if err != nil { + return err + } +@@ -553,7 +564,6 @@ func (hs *clientHandshakeState) doFullHandshake() error { + certReq, ok := msg.(*certificateRequestMsg) + if ok { + certRequested = true +- hs.finishedHash.Write(certReq.marshal()) + + cri := certificateRequestInfoFromMsg(hs.ctx, c.vers, certReq) + if chainToSend, err = c.getClientCertificate(cri); err != nil { +@@ -561,7 +571,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { + return err + } + +- msg, err = c.readHandshake() ++ msg, err = c.readHandshake(&hs.finishedHash) + if err != nil { + return err + } +@@ -572,7 +582,6 @@ func (hs *clientHandshakeState) doFullHandshake() error { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(shd, msg) + } +- hs.finishedHash.Write(shd.marshal()) + + // If the server requested a certificate then we have to send a + // Certificate message, even if it's empty because we don't have a +@@ -580,8 +589,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { + if certRequested { + certMsg = new(certificateMsg) + certMsg.certificates = chainToSend.Certificate +- hs.finishedHash.Write(certMsg.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certMsg, &hs.finishedHash); err != nil { + return err + } + } +@@ -592,8 +600,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { + return err + } + if ckx != nil { +- hs.finishedHash.Write(ckx.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(ckx, &hs.finishedHash); err != nil { + return err + } + } +@@ -640,8 +647,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { + return err + } + +- hs.finishedHash.Write(certVerify.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certVerify, &hs.finishedHash); err != nil { + return err + } + } +@@ -776,7 +782,10 @@ func (hs *clientHandshakeState) readFinished(out []byte) error { + return err + } + +- msg, err := c.readHandshake() ++ // finishedMsg is included in the transcript, but not until after we ++ // check the client version, since the state before this message was ++ // sent is used during verification. ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +@@ -792,7 +801,11 @@ func (hs *clientHandshakeState) readFinished(out []byte) error { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: server's Finished message was incorrect") + } +- hs.finishedHash.Write(serverFinished.marshal()) ++ ++ if err := transcriptMsg(serverFinished, &hs.finishedHash); err != nil { ++ return err ++ } ++ + copy(out, verify) + return nil + } +@@ -803,7 +816,7 @@ func (hs *clientHandshakeState) readSessionTicket() error { + } + + c := hs.c +- msg, err := c.readHandshake() ++ msg, err := c.readHandshake(&hs.finishedHash) + if err != nil { + return err + } +@@ -812,7 +825,6 @@ func (hs *clientHandshakeState) readSessionTicket() error { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(sessionTicketMsg, msg) + } +- hs.finishedHash.Write(sessionTicketMsg.marshal()) + + hs.session = &ClientSessionState{ + sessionTicket: sessionTicketMsg.ticket, +@@ -832,14 +844,13 @@ func (hs *clientHandshakeState) readSessionTicket() error { + func (hs *clientHandshakeState) sendFinished(out []byte) error { + c := hs.c + +- if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { ++ if err := c.writeChangeCipherRecord(); err != nil { + return err + } + + finished := new(finishedMsg) + finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret) +- hs.finishedHash.Write(finished.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(finished, &hs.finishedHash); err != nil { + return err + } + copy(out, finished.verifyData) +diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go +index 380de9f6fb..749c9fc954 100644 +--- a/src/crypto/tls/handshake_client_test.go ++++ b/src/crypto/tls/handshake_client_test.go +@@ -1257,7 +1257,7 @@ func TestServerSelectingUnconfiguredApplicationProtocol(t *testing.T) { + cipherSuite: TLS_RSA_WITH_AES_128_GCM_SHA256, + alpnProtocol: "how-about-this", + } +- serverHelloBytes := serverHello.marshal() ++ serverHelloBytes := mustMarshal(t, serverHello) + + s.Write([]byte{ + byte(recordTypeHandshake), +@@ -1500,7 +1500,7 @@ func TestServerSelectingUnconfiguredCipherSuite(t *testing.T) { + random: make([]byte, 32), + cipherSuite: TLS_RSA_WITH_AES_256_GCM_SHA384, + } +- serverHelloBytes := serverHello.marshal() ++ serverHelloBytes := mustMarshal(t, serverHello) + + s.Write([]byte{ + byte(recordTypeHandshake), +diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go +index c7989867f5..0f2dee42de 100644 +--- a/src/crypto/tls/handshake_client_tls13.go ++++ b/src/crypto/tls/handshake_client_tls13.go +@@ -62,7 +62,10 @@ func (hs *clientHandshakeStateTLS13) handshake() error { + } + + hs.transcript = hs.suite.hash.New() +- hs.transcript.Write(hs.hello.marshal()) ++ ++ if err := transcriptMsg(hs.hello, hs.transcript); err != nil { ++ return err ++ } + + if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { + if err := hs.sendDummyChangeCipherSpec(); err != nil { +@@ -73,7 +76,9 @@ func (hs *clientHandshakeStateTLS13) handshake() error { + } + } + +- hs.transcript.Write(hs.serverHello.marshal()) ++ if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { ++ return err ++ } + + c.buffering = true + if err := hs.processServerHello(); err != nil { +@@ -172,8 +177,7 @@ func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error { + } + hs.sentDummyCCS = true + +- _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) +- return err ++ return hs.c.writeChangeCipherRecord() + } + + // processHelloRetryRequest handles the HRR in hs.serverHello, modifies and +@@ -188,7 +192,9 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { + hs.transcript.Reset() + hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) + hs.transcript.Write(chHash) +- hs.transcript.Write(hs.serverHello.marshal()) ++ if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { ++ return err ++ } + + // The only HelloRetryRequest extensions we support are key_share and + // cookie, and clients must abort the handshake if the HRR would not result +@@ -253,10 +259,18 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { + transcript := hs.suite.hash.New() + transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) + transcript.Write(chHash) +- transcript.Write(hs.serverHello.marshal()) +- transcript.Write(hs.hello.marshalWithoutBinders()) ++ if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { ++ return err ++ } ++ helloBytes, err := hs.hello.marshalWithoutBinders() ++ if err != nil { ++ return err ++ } ++ transcript.Write(helloBytes) + pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)} +- hs.hello.updateBinders(pskBinders) ++ if err := hs.hello.updateBinders(pskBinders); err != nil { ++ return err ++ } + } else { + // Server selected a cipher suite incompatible with the PSK. + hs.hello.pskIdentities = nil +@@ -264,12 +278,12 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { + } + } + +- hs.transcript.Write(hs.hello.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil { + return err + } + +- msg, err := c.readHandshake() ++ // serverHelloMsg is not included in the transcript ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +@@ -358,6 +372,7 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { + if !hs.usingPSK { + earlySecret = hs.suite.extract(nil, nil) + } ++ + handshakeSecret := hs.suite.extract(sharedKey, + hs.suite.deriveSecret(earlySecret, "derived", nil)) + +@@ -388,7 +403,7 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { + func (hs *clientHandshakeStateTLS13) readServerParameters() error { + c := hs.c + +- msg, err := c.readHandshake() ++ msg, err := c.readHandshake(hs.transcript) + if err != nil { + return err + } +@@ -398,7 +413,6 @@ func (hs *clientHandshakeStateTLS13) readServerParameters() error { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(encryptedExtensions, msg) + } +- hs.transcript.Write(encryptedExtensions.marshal()) + + if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil { + c.sendAlert(alertUnsupportedExtension) +@@ -427,18 +441,16 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { + return nil + } + +- msg, err := c.readHandshake() ++ msg, err := c.readHandshake(hs.transcript) + if err != nil { + return err + } + + certReq, ok := msg.(*certificateRequestMsgTLS13) + if ok { +- hs.transcript.Write(certReq.marshal()) +- + hs.certReq = certReq + +- msg, err = c.readHandshake() ++ msg, err = c.readHandshake(hs.transcript) + if err != nil { + return err + } +@@ -453,7 +465,6 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { + c.sendAlert(alertDecodeError) + return errors.New("tls: received empty certificates message") + } +- hs.transcript.Write(certMsg.marshal()) + + c.scts = certMsg.certificate.SignedCertificateTimestamps + c.ocspResponse = certMsg.certificate.OCSPStaple +@@ -462,7 +473,10 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { + return err + } + +- msg, err = c.readHandshake() ++ // certificateVerifyMsg is included in the transcript, but not until ++ // after we verify the handshake signature, since the state before ++ // this message was sent is used. ++ msg, err = c.readHandshake(nil) + if err != nil { + return err + } +@@ -493,7 +507,9 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { + return errors.New("tls: invalid signature by the server certificate: " + err.Error()) + } + +- hs.transcript.Write(certVerify.marshal()) ++ if err := transcriptMsg(certVerify, hs.transcript); err != nil { ++ return err ++ } + + return nil + } +@@ -501,7 +517,10 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { + func (hs *clientHandshakeStateTLS13) readServerFinished() error { + c := hs.c + +- msg, err := c.readHandshake() ++ // finishedMsg is included in the transcript, but not until after we ++ // check the client version, since the state before this message was ++ // sent is used during verification. ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +@@ -518,7 +537,9 @@ func (hs *clientHandshakeStateTLS13) readServerFinished() error { + return errors.New("tls: invalid server finished hash") + } + +- hs.transcript.Write(finished.marshal()) ++ if err := transcriptMsg(finished, hs.transcript); err != nil { ++ return err ++ } + + // Derive secrets that take context through the server Finished. + +@@ -567,8 +588,7 @@ func (hs *clientHandshakeStateTLS13) sendClientCertificate() error { + certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0 + certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0 + +- hs.transcript.Write(certMsg.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certMsg, hs.transcript); err != nil { + return err + } + +@@ -605,8 +625,7 @@ func (hs *clientHandshakeStateTLS13) sendClientCertificate() error { + } + certVerifyMsg.signature = sig + +- hs.transcript.Write(certVerifyMsg.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certVerifyMsg, hs.transcript); err != nil { + return err + } + +@@ -620,8 +639,7 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error { + verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), + } + +- hs.transcript.Write(finished.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(finished, hs.transcript); err != nil { + return err + } + +diff --git a/src/crypto/tls/handshake_messages.go b/src/crypto/tls/handshake_messages.go +index 7ab0f100b8..695aacf126 100644 +--- a/src/crypto/tls/handshake_messages.go ++++ b/src/crypto/tls/handshake_messages.go +@@ -5,6 +5,7 @@ + package tls + + import ( ++ "errors" + "fmt" + "strings" + +@@ -94,9 +95,181 @@ type clientHelloMsg struct { + pskBinders [][]byte + } + +-func (m *clientHelloMsg) marshal() []byte { ++func (m *clientHelloMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil ++ } ++ ++ var exts cryptobyte.Builder ++ if len(m.serverName) > 0 { ++ // RFC 6066, Section 3 ++ exts.AddUint16(extensionServerName) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8(0) // name_type = host_name ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes([]byte(m.serverName)) ++ }) ++ }) ++ }) ++ } ++ if m.ocspStapling { ++ // RFC 4366, Section 3.6 ++ exts.AddUint16(extensionStatusRequest) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8(1) // status_type = ocsp ++ exts.AddUint16(0) // empty responder_id_list ++ exts.AddUint16(0) // empty request_extensions ++ }) ++ } ++ if len(m.supportedCurves) > 0 { ++ // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7 ++ exts.AddUint16(extensionSupportedCurves) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, curve := range m.supportedCurves { ++ exts.AddUint16(uint16(curve)) ++ } ++ }) ++ }) ++ } ++ if len(m.supportedPoints) > 0 { ++ // RFC 4492, Section 5.1.2 ++ exts.AddUint16(extensionSupportedPoints) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.supportedPoints) ++ }) ++ }) ++ } ++ if m.ticketSupported { ++ // RFC 5077, Section 3.2 ++ exts.AddUint16(extensionSessionTicket) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.sessionTicket) ++ }) ++ } ++ if len(m.supportedSignatureAlgorithms) > 0 { ++ // RFC 5246, Section 7.4.1.4.1 ++ exts.AddUint16(extensionSignatureAlgorithms) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, sigAlgo := range m.supportedSignatureAlgorithms { ++ exts.AddUint16(uint16(sigAlgo)) ++ } ++ }) ++ }) ++ } ++ if len(m.supportedSignatureAlgorithmsCert) > 0 { ++ // RFC 8446, Section 4.2.3 ++ exts.AddUint16(extensionSignatureAlgorithmsCert) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, sigAlgo := range m.supportedSignatureAlgorithmsCert { ++ exts.AddUint16(uint16(sigAlgo)) ++ } ++ }) ++ }) ++ } ++ if m.secureRenegotiationSupported { ++ // RFC 5746, Section 3.2 ++ exts.AddUint16(extensionRenegotiationInfo) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.secureRenegotiation) ++ }) ++ }) ++ } ++ if len(m.alpnProtocols) > 0 { ++ // RFC 7301, Section 3.1 ++ exts.AddUint16(extensionALPN) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, proto := range m.alpnProtocols { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes([]byte(proto)) ++ }) ++ } ++ }) ++ }) ++ } ++ if m.scts { ++ // RFC 6962, Section 3.3.1 ++ exts.AddUint16(extensionSCT) ++ exts.AddUint16(0) // empty extension_data ++ } ++ if len(m.supportedVersions) > 0 { ++ // RFC 8446, Section 4.2.1 ++ exts.AddUint16(extensionSupportedVersions) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, vers := range m.supportedVersions { ++ exts.AddUint16(vers) ++ } ++ }) ++ }) ++ } ++ if len(m.cookie) > 0 { ++ // RFC 8446, Section 4.2.2 ++ exts.AddUint16(extensionCookie) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.cookie) ++ }) ++ }) ++ } ++ if len(m.keyShares) > 0 { ++ // RFC 8446, Section 4.2.8 ++ exts.AddUint16(extensionKeyShare) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, ks := range m.keyShares { ++ exts.AddUint16(uint16(ks.group)) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(ks.data) ++ }) ++ } ++ }) ++ }) ++ } ++ if m.earlyData { ++ // RFC 8446, Section 4.2.10 ++ exts.AddUint16(extensionEarlyData) ++ exts.AddUint16(0) // empty extension_data ++ } ++ if len(m.pskModes) > 0 { ++ // RFC 8446, Section 4.2.9 ++ exts.AddUint16(extensionPSKModes) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.pskModes) ++ }) ++ }) ++ } ++ if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension ++ // RFC 8446, Section 4.2.11 ++ exts.AddUint16(extensionPreSharedKey) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, psk := range m.pskIdentities { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(psk.label) ++ }) ++ exts.AddUint32(psk.obfuscatedTicketAge) ++ } ++ }) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, binder := range m.pskBinders { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(binder) ++ }) ++ } ++ }) ++ }) ++ } ++ extBytes, err := exts.Bytes() ++ if err != nil { ++ return nil, err + } + + var b cryptobyte.Builder +@@ -116,219 +289,53 @@ func (m *clientHelloMsg) marshal() []byte { + b.AddBytes(m.compressionMethods) + }) + +- // If extensions aren't present, omit them. +- var extensionsPresent bool +- bWithoutExtensions := *b +- +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- if len(m.serverName) > 0 { +- // RFC 6066, Section 3 +- b.AddUint16(extensionServerName) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8(0) // name_type = host_name +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes([]byte(m.serverName)) +- }) +- }) +- }) +- } +- if m.ocspStapling { +- // RFC 4366, Section 3.6 +- b.AddUint16(extensionStatusRequest) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8(1) // status_type = ocsp +- b.AddUint16(0) // empty responder_id_list +- b.AddUint16(0) // empty request_extensions +- }) +- } +- if len(m.supportedCurves) > 0 { +- // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7 +- b.AddUint16(extensionSupportedCurves) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, curve := range m.supportedCurves { +- b.AddUint16(uint16(curve)) +- } +- }) +- }) +- } +- if len(m.supportedPoints) > 0 { +- // RFC 4492, Section 5.1.2 +- b.AddUint16(extensionSupportedPoints) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.supportedPoints) +- }) +- }) +- } +- if m.ticketSupported { +- // RFC 5077, Section 3.2 +- b.AddUint16(extensionSessionTicket) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.sessionTicket) +- }) +- } +- if len(m.supportedSignatureAlgorithms) > 0 { +- // RFC 5246, Section 7.4.1.4.1 +- b.AddUint16(extensionSignatureAlgorithms) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, sigAlgo := range m.supportedSignatureAlgorithms { +- b.AddUint16(uint16(sigAlgo)) +- } +- }) +- }) +- } +- if len(m.supportedSignatureAlgorithmsCert) > 0 { +- // RFC 8446, Section 4.2.3 +- b.AddUint16(extensionSignatureAlgorithmsCert) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, sigAlgo := range m.supportedSignatureAlgorithmsCert { +- b.AddUint16(uint16(sigAlgo)) +- } +- }) +- }) +- } +- if m.secureRenegotiationSupported { +- // RFC 5746, Section 3.2 +- b.AddUint16(extensionRenegotiationInfo) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.secureRenegotiation) +- }) +- }) +- } +- if len(m.alpnProtocols) > 0 { +- // RFC 7301, Section 3.1 +- b.AddUint16(extensionALPN) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, proto := range m.alpnProtocols { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes([]byte(proto)) +- }) +- } +- }) +- }) +- } +- if m.scts { +- // RFC 6962, Section 3.3.1 +- b.AddUint16(extensionSCT) +- b.AddUint16(0) // empty extension_data +- } +- if len(m.supportedVersions) > 0 { +- // RFC 8446, Section 4.2.1 +- b.AddUint16(extensionSupportedVersions) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, vers := range m.supportedVersions { +- b.AddUint16(vers) +- } +- }) +- }) +- } +- if len(m.cookie) > 0 { +- // RFC 8446, Section 4.2.2 +- b.AddUint16(extensionCookie) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.cookie) +- }) +- }) +- } +- if len(m.keyShares) > 0 { +- // RFC 8446, Section 4.2.8 +- b.AddUint16(extensionKeyShare) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, ks := range m.keyShares { +- b.AddUint16(uint16(ks.group)) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(ks.data) +- }) +- } +- }) +- }) +- } +- if m.earlyData { +- // RFC 8446, Section 4.2.10 +- b.AddUint16(extensionEarlyData) +- b.AddUint16(0) // empty extension_data +- } +- if len(m.pskModes) > 0 { +- // RFC 8446, Section 4.2.9 +- b.AddUint16(extensionPSKModes) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.pskModes) +- }) +- }) +- } +- if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension +- // RFC 8446, Section 4.2.11 +- b.AddUint16(extensionPreSharedKey) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, psk := range m.pskIdentities { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(psk.label) +- }) +- b.AddUint32(psk.obfuscatedTicketAge) +- } +- }) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, binder := range m.pskBinders { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(binder) +- }) +- } +- }) +- }) +- } +- +- extensionsPresent = len(b.BytesOrPanic()) > 2 +- }) +- +- if !extensionsPresent { +- *b = bWithoutExtensions ++ if len(extBytes) > 0 { ++ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { ++ b.AddBytes(extBytes) ++ }) + } + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + // marshalWithoutBinders returns the ClientHello through the + // PreSharedKeyExtension.identities field, according to RFC 8446, Section + // 4.2.11.2. Note that m.pskBinders must be set to slices of the correct length. +-func (m *clientHelloMsg) marshalWithoutBinders() []byte { ++func (m *clientHelloMsg) marshalWithoutBinders() ([]byte, error) { + bindersLen := 2 // uint16 length prefix + for _, binder := range m.pskBinders { + bindersLen += 1 // uint8 length prefix + bindersLen += len(binder) + } + +- fullMessage := m.marshal() +- return fullMessage[:len(fullMessage)-bindersLen] ++ fullMessage, err := m.marshal() ++ if err != nil { ++ return nil, err ++ } ++ return fullMessage[:len(fullMessage)-bindersLen], nil + } + + // updateBinders updates the m.pskBinders field, if necessary updating the + // cached marshaled representation. The supplied binders must have the same + // length as the current m.pskBinders. +-func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) { ++func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) error { + if len(pskBinders) != len(m.pskBinders) { +- panic("tls: internal error: pskBinders length mismatch") ++ return errors.New("tls: internal error: pskBinders length mismatch") + } + for i := range m.pskBinders { + if len(pskBinders[i]) != len(m.pskBinders[i]) { +- panic("tls: internal error: pskBinders length mismatch") ++ return errors.New("tls: internal error: pskBinders length mismatch") + } + } + m.pskBinders = pskBinders + if m.raw != nil { +- lenWithoutBinders := len(m.marshalWithoutBinders()) ++ helloBytes, err := m.marshalWithoutBinders() ++ if err != nil { ++ return err ++ } ++ lenWithoutBinders := len(helloBytes) + b := cryptobyte.NewFixedBuilder(m.raw[:lenWithoutBinders]) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, binder := range m.pskBinders { +@@ -338,9 +345,11 @@ func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) { + } + }) + if out, err := b.Bytes(); err != nil || len(out) != len(m.raw) { +- panic("tls: internal error: failed to update binders") ++ return errors.New("tls: internal error: failed to update binders") + } + } ++ ++ return nil + } + + func (m *clientHelloMsg) unmarshal(data []byte) bool { +@@ -618,9 +627,98 @@ type serverHelloMsg struct { + selectedGroup CurveID + } + +-func (m *serverHelloMsg) marshal() []byte { ++func (m *serverHelloMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil ++ } ++ ++ var exts cryptobyte.Builder ++ if m.ocspStapling { ++ exts.AddUint16(extensionStatusRequest) ++ exts.AddUint16(0) // empty extension_data ++ } ++ if m.ticketSupported { ++ exts.AddUint16(extensionSessionTicket) ++ exts.AddUint16(0) // empty extension_data ++ } ++ if m.secureRenegotiationSupported { ++ exts.AddUint16(extensionRenegotiationInfo) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.secureRenegotiation) ++ }) ++ }) ++ } ++ if len(m.alpnProtocol) > 0 { ++ exts.AddUint16(extensionALPN) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes([]byte(m.alpnProtocol)) ++ }) ++ }) ++ }) ++ } ++ if len(m.scts) > 0 { ++ exts.AddUint16(extensionSCT) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ for _, sct := range m.scts { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(sct) ++ }) ++ } ++ }) ++ }) ++ } ++ if m.supportedVersion != 0 { ++ exts.AddUint16(extensionSupportedVersions) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16(m.supportedVersion) ++ }) ++ } ++ if m.serverShare.group != 0 { ++ exts.AddUint16(extensionKeyShare) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16(uint16(m.serverShare.group)) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.serverShare.data) ++ }) ++ }) ++ } ++ if m.selectedIdentityPresent { ++ exts.AddUint16(extensionPreSharedKey) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16(m.selectedIdentity) ++ }) ++ } ++ ++ if len(m.cookie) > 0 { ++ exts.AddUint16(extensionCookie) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.cookie) ++ }) ++ }) ++ } ++ if m.selectedGroup != 0 { ++ exts.AddUint16(extensionKeyShare) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint16(uint16(m.selectedGroup)) ++ }) ++ } ++ if len(m.supportedPoints) > 0 { ++ exts.AddUint16(extensionSupportedPoints) ++ exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { ++ exts.AddBytes(m.supportedPoints) ++ }) ++ }) ++ } ++ ++ extBytes, err := exts.Bytes() ++ if err != nil { ++ return nil, err + } + + var b cryptobyte.Builder +@@ -634,104 +732,15 @@ func (m *serverHelloMsg) marshal() []byte { + b.AddUint16(m.cipherSuite) + b.AddUint8(m.compressionMethod) + +- // If extensions aren't present, omit them. +- var extensionsPresent bool +- bWithoutExtensions := *b +- +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- if m.ocspStapling { +- b.AddUint16(extensionStatusRequest) +- b.AddUint16(0) // empty extension_data +- } +- if m.ticketSupported { +- b.AddUint16(extensionSessionTicket) +- b.AddUint16(0) // empty extension_data +- } +- if m.secureRenegotiationSupported { +- b.AddUint16(extensionRenegotiationInfo) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.secureRenegotiation) +- }) +- }) +- } +- if len(m.alpnProtocol) > 0 { +- b.AddUint16(extensionALPN) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes([]byte(m.alpnProtocol)) +- }) +- }) +- }) +- } +- if len(m.scts) > 0 { +- b.AddUint16(extensionSCT) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- for _, sct := range m.scts { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(sct) +- }) +- } +- }) +- }) +- } +- if m.supportedVersion != 0 { +- b.AddUint16(extensionSupportedVersions) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16(m.supportedVersion) +- }) +- } +- if m.serverShare.group != 0 { +- b.AddUint16(extensionKeyShare) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16(uint16(m.serverShare.group)) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.serverShare.data) +- }) +- }) +- } +- if m.selectedIdentityPresent { +- b.AddUint16(extensionPreSharedKey) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16(m.selectedIdentity) +- }) +- } +- +- if len(m.cookie) > 0 { +- b.AddUint16(extensionCookie) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.cookie) +- }) +- }) +- } +- if m.selectedGroup != 0 { +- b.AddUint16(extensionKeyShare) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint16(uint16(m.selectedGroup)) +- }) +- } +- if len(m.supportedPoints) > 0 { +- b.AddUint16(extensionSupportedPoints) +- b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { +- b.AddBytes(m.supportedPoints) +- }) +- }) +- } +- +- extensionsPresent = len(b.BytesOrPanic()) > 2 +- }) +- +- if !extensionsPresent { +- *b = bWithoutExtensions ++ if len(extBytes) > 0 { ++ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { ++ b.AddBytes(extBytes) ++ }) + } + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func (m *serverHelloMsg) unmarshal(data []byte) bool { +@@ -855,9 +864,9 @@ type encryptedExtensionsMsg struct { + alpnProtocol string + } + +-func (m *encryptedExtensionsMsg) marshal() []byte { ++func (m *encryptedExtensionsMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var b cryptobyte.Builder +@@ -877,8 +886,9 @@ func (m *encryptedExtensionsMsg) marshal() []byte { + }) + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ var err error ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { +@@ -926,10 +936,10 @@ func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { + + type endOfEarlyDataMsg struct{} + +-func (m *endOfEarlyDataMsg) marshal() []byte { ++func (m *endOfEarlyDataMsg) marshal() ([]byte, error) { + x := make([]byte, 4) + x[0] = typeEndOfEarlyData +- return x ++ return x, nil + } + + func (m *endOfEarlyDataMsg) unmarshal(data []byte) bool { +@@ -941,9 +951,9 @@ type keyUpdateMsg struct { + updateRequested bool + } + +-func (m *keyUpdateMsg) marshal() []byte { ++func (m *keyUpdateMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var b cryptobyte.Builder +@@ -956,8 +966,9 @@ func (m *keyUpdateMsg) marshal() []byte { + } + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ var err error ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func (m *keyUpdateMsg) unmarshal(data []byte) bool { +@@ -989,9 +1000,9 @@ type newSessionTicketMsgTLS13 struct { + maxEarlyData uint32 + } + +-func (m *newSessionTicketMsgTLS13) marshal() []byte { ++func (m *newSessionTicketMsgTLS13) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var b cryptobyte.Builder +@@ -1016,8 +1027,9 @@ func (m *newSessionTicketMsgTLS13) marshal() []byte { + }) + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ var err error ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool { +@@ -1070,9 +1082,9 @@ type certificateRequestMsgTLS13 struct { + certificateAuthorities [][]byte + } + +-func (m *certificateRequestMsgTLS13) marshal() []byte { ++func (m *certificateRequestMsgTLS13) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var b cryptobyte.Builder +@@ -1131,8 +1143,9 @@ func (m *certificateRequestMsgTLS13) marshal() []byte { + }) + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ var err error ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool { +@@ -1216,9 +1229,9 @@ type certificateMsg struct { + certificates [][]byte + } + +-func (m *certificateMsg) marshal() (x []byte) { ++func (m *certificateMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var i int +@@ -1227,7 +1240,7 @@ func (m *certificateMsg) marshal() (x []byte) { + } + + length := 3 + 3*len(m.certificates) + i +- x = make([]byte, 4+length) ++ x := make([]byte, 4+length) + x[0] = typeCertificate + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) +@@ -1248,7 +1261,7 @@ func (m *certificateMsg) marshal() (x []byte) { + } + + m.raw = x +- return ++ return m.raw, nil + } + + func (m *certificateMsg) unmarshal(data []byte) bool { +@@ -1295,9 +1308,9 @@ type certificateMsgTLS13 struct { + scts bool + } + +-func (m *certificateMsgTLS13) marshal() []byte { ++func (m *certificateMsgTLS13) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var b cryptobyte.Builder +@@ -1315,8 +1328,9 @@ func (m *certificateMsgTLS13) marshal() []byte { + marshalCertificate(b, certificate) + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ var err error ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func marshalCertificate(b *cryptobyte.Builder, certificate Certificate) { +@@ -1439,9 +1453,9 @@ type serverKeyExchangeMsg struct { + key []byte + } + +-func (m *serverKeyExchangeMsg) marshal() []byte { ++func (m *serverKeyExchangeMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + length := len(m.key) + x := make([]byte, length+4) +@@ -1452,7 +1466,7 @@ func (m *serverKeyExchangeMsg) marshal() []byte { + copy(x[4:], m.key) + + m.raw = x +- return x ++ return x, nil + } + + func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool { +@@ -1469,9 +1483,9 @@ type certificateStatusMsg struct { + response []byte + } + +-func (m *certificateStatusMsg) marshal() []byte { ++func (m *certificateStatusMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var b cryptobyte.Builder +@@ -1483,8 +1497,9 @@ func (m *certificateStatusMsg) marshal() []byte { + }) + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ var err error ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func (m *certificateStatusMsg) unmarshal(data []byte) bool { +@@ -1503,10 +1518,10 @@ func (m *certificateStatusMsg) unmarshal(data []byte) bool { + + type serverHelloDoneMsg struct{} + +-func (m *serverHelloDoneMsg) marshal() []byte { ++func (m *serverHelloDoneMsg) marshal() ([]byte, error) { + x := make([]byte, 4) + x[0] = typeServerHelloDone +- return x ++ return x, nil + } + + func (m *serverHelloDoneMsg) unmarshal(data []byte) bool { +@@ -1518,9 +1533,9 @@ type clientKeyExchangeMsg struct { + ciphertext []byte + } + +-func (m *clientKeyExchangeMsg) marshal() []byte { ++func (m *clientKeyExchangeMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + length := len(m.ciphertext) + x := make([]byte, length+4) +@@ -1531,7 +1546,7 @@ func (m *clientKeyExchangeMsg) marshal() []byte { + copy(x[4:], m.ciphertext) + + m.raw = x +- return x ++ return x, nil + } + + func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool { +@@ -1552,9 +1567,9 @@ type finishedMsg struct { + verifyData []byte + } + +-func (m *finishedMsg) marshal() []byte { ++func (m *finishedMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var b cryptobyte.Builder +@@ -1563,8 +1578,9 @@ func (m *finishedMsg) marshal() []byte { + b.AddBytes(m.verifyData) + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ var err error ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func (m *finishedMsg) unmarshal(data []byte) bool { +@@ -1586,9 +1602,9 @@ type certificateRequestMsg struct { + certificateAuthorities [][]byte + } + +-func (m *certificateRequestMsg) marshal() (x []byte) { ++func (m *certificateRequestMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + // See RFC 4346, Section 7.4.4. +@@ -1603,7 +1619,7 @@ func (m *certificateRequestMsg) marshal() (x []byte) { + length += 2 + 2*len(m.supportedSignatureAlgorithms) + } + +- x = make([]byte, 4+length) ++ x := make([]byte, 4+length) + x[0] = typeCertificateRequest + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) +@@ -1638,7 +1654,7 @@ func (m *certificateRequestMsg) marshal() (x []byte) { + } + + m.raw = x +- return ++ return m.raw, nil + } + + func (m *certificateRequestMsg) unmarshal(data []byte) bool { +@@ -1724,9 +1740,9 @@ type certificateVerifyMsg struct { + signature []byte + } + +-func (m *certificateVerifyMsg) marshal() (x []byte) { ++func (m *certificateVerifyMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + var b cryptobyte.Builder +@@ -1740,8 +1756,9 @@ func (m *certificateVerifyMsg) marshal() (x []byte) { + }) + }) + +- m.raw = b.BytesOrPanic() +- return m.raw ++ var err error ++ m.raw, err = b.Bytes() ++ return m.raw, err + } + + func (m *certificateVerifyMsg) unmarshal(data []byte) bool { +@@ -1764,15 +1781,15 @@ type newSessionTicketMsg struct { + ticket []byte + } + +-func (m *newSessionTicketMsg) marshal() (x []byte) { ++func (m *newSessionTicketMsg) marshal() ([]byte, error) { + if m.raw != nil { +- return m.raw ++ return m.raw, nil + } + + // See RFC 5077, Section 3.3. + ticketLen := len(m.ticket) + length := 2 + 4 + ticketLen +- x = make([]byte, 4+length) ++ x := make([]byte, 4+length) + x[0] = typeNewSessionTicket + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) +@@ -1783,7 +1800,7 @@ func (m *newSessionTicketMsg) marshal() (x []byte) { + + m.raw = x + +- return ++ return m.raw, nil + } + + func (m *newSessionTicketMsg) unmarshal(data []byte) bool { +@@ -1811,10 +1828,25 @@ func (m *newSessionTicketMsg) unmarshal(data []byte) bool { + type helloRequestMsg struct { + } + +-func (*helloRequestMsg) marshal() []byte { +- return []byte{typeHelloRequest, 0, 0, 0} ++func (*helloRequestMsg) marshal() ([]byte, error) { ++ return []byte{typeHelloRequest, 0, 0, 0}, nil + } + + func (*helloRequestMsg) unmarshal(data []byte) bool { + return len(data) == 4 + } ++ ++type transcriptHash interface { ++ Write([]byte) (int, error) ++} ++ ++// transcriptMsg is a helper used to marshal and hash messages which typically ++// are not written to the wire, and as such aren't hashed during Conn.writeRecord. ++func transcriptMsg(msg handshakeMessage, h transcriptHash) error { ++ data, err := msg.marshal() ++ if err != nil { ++ return err ++ } ++ h.Write(data) ++ return nil ++} +diff --git a/src/crypto/tls/handshake_messages_test.go b/src/crypto/tls/handshake_messages_test.go +index c6fc8f2bf3..206e2fb024 100644 +--- a/src/crypto/tls/handshake_messages_test.go ++++ b/src/crypto/tls/handshake_messages_test.go +@@ -38,6 +38,15 @@ var tests = []any{ + &certificateMsgTLS13{}, + } + ++func mustMarshal(t *testing.T, msg handshakeMessage) []byte { ++ t.Helper() ++ b, err := msg.marshal() ++ if err != nil { ++ t.Fatal(err) ++ } ++ return b ++} ++ + func TestMarshalUnmarshal(t *testing.T) { + rand := rand.New(rand.NewSource(time.Now().UnixNano())) + +@@ -56,7 +65,7 @@ func TestMarshalUnmarshal(t *testing.T) { + } + + m1 := v.Interface().(handshakeMessage) +- marshaled := m1.marshal() ++ marshaled := mustMarshal(t, m1) + m2 := iface.(handshakeMessage) + if !m2.unmarshal(marshaled) { + t.Errorf("#%d failed to unmarshal %#v %x", i, m1, marshaled) +@@ -409,12 +418,12 @@ func TestRejectEmptySCTList(t *testing.T) { + + var random [32]byte + sct := []byte{0x42, 0x42, 0x42, 0x42} +- serverHello := serverHelloMsg{ ++ serverHello := &serverHelloMsg{ + vers: VersionTLS12, + random: random[:], + scts: [][]byte{sct}, + } +- serverHelloBytes := serverHello.marshal() ++ serverHelloBytes := mustMarshal(t, serverHello) + + var serverHelloCopy serverHelloMsg + if !serverHelloCopy.unmarshal(serverHelloBytes) { +@@ -452,12 +461,12 @@ func TestRejectEmptySCT(t *testing.T) { + // not be zero length. + + var random [32]byte +- serverHello := serverHelloMsg{ ++ serverHello := &serverHelloMsg{ + vers: VersionTLS12, + random: random[:], + scts: [][]byte{nil}, + } +- serverHelloBytes := serverHello.marshal() ++ serverHelloBytes := mustMarshal(t, serverHello) + + var serverHelloCopy serverHelloMsg + if serverHelloCopy.unmarshal(serverHelloBytes) { +diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go +index 92b38cb11b..8cb9acf528 100644 +--- a/src/crypto/tls/handshake_server.go ++++ b/src/crypto/tls/handshake_server.go +@@ -129,7 +129,9 @@ func (hs *serverHandshakeState) handshake() error { + + // readClientHello reads a ClientHello message and selects the protocol version. + func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { +- msg, err := c.readHandshake() ++ // clientHelloMsg is included in the transcript, but we haven't initialized ++ // it yet. The respective handshake functions will record it themselves. ++ msg, err := c.readHandshake(nil) + if err != nil { + return nil, err + } +@@ -463,9 +465,10 @@ func (hs *serverHandshakeState) doResumeHandshake() error { + hs.hello.ticketSupported = hs.sessionState.usedOldKey + hs.finishedHash = newFinishedHash(c.vers, hs.suite) + hs.finishedHash.discardHandshakeBuffer() +- hs.finishedHash.Write(hs.clientHello.marshal()) +- hs.finishedHash.Write(hs.hello.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { ++ if err := transcriptMsg(hs.clientHello, &hs.finishedHash); err != nil { ++ return err ++ } ++ if _, err := hs.c.writeHandshakeRecord(hs.hello, &hs.finishedHash); err != nil { + return err + } + +@@ -503,24 +506,23 @@ func (hs *serverHandshakeState) doFullHandshake() error { + // certificates won't be used. + hs.finishedHash.discardHandshakeBuffer() + } +- hs.finishedHash.Write(hs.clientHello.marshal()) +- hs.finishedHash.Write(hs.hello.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { ++ if err := transcriptMsg(hs.clientHello, &hs.finishedHash); err != nil { ++ return err ++ } ++ if _, err := hs.c.writeHandshakeRecord(hs.hello, &hs.finishedHash); err != nil { + return err + } + + certMsg := new(certificateMsg) + certMsg.certificates = hs.cert.Certificate +- hs.finishedHash.Write(certMsg.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certMsg, &hs.finishedHash); err != nil { + return err + } + + if hs.hello.ocspStapling { + certStatus := new(certificateStatusMsg) + certStatus.response = hs.cert.OCSPStaple +- hs.finishedHash.Write(certStatus.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certStatus, &hs.finishedHash); err != nil { + return err + } + } +@@ -532,8 +534,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { + return err + } + if skx != nil { +- hs.finishedHash.Write(skx.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil { + return err + } + } +@@ -559,15 +560,13 @@ func (hs *serverHandshakeState) doFullHandshake() error { + if c.config.ClientCAs != nil { + certReq.certificateAuthorities = c.config.ClientCAs.Subjects() + } +- hs.finishedHash.Write(certReq.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certReq, &hs.finishedHash); err != nil { + return err + } + } + + helloDone := new(serverHelloDoneMsg) +- hs.finishedHash.Write(helloDone.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(helloDone, &hs.finishedHash); err != nil { + return err + } + +@@ -577,7 +576,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { + + var pub crypto.PublicKey // public key for client auth, if any + +- msg, err := c.readHandshake() ++ msg, err := c.readHandshake(&hs.finishedHash) + if err != nil { + return err + } +@@ -590,7 +589,6 @@ func (hs *serverHandshakeState) doFullHandshake() error { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } +- hs.finishedHash.Write(certMsg.marshal()) + + if err := c.processCertsFromClient(Certificate{ + Certificate: certMsg.certificates, +@@ -601,7 +599,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { + pub = c.peerCertificates[0].PublicKey + } + +- msg, err = c.readHandshake() ++ msg, err = c.readHandshake(&hs.finishedHash) + if err != nil { + return err + } +@@ -619,7 +617,6 @@ func (hs *serverHandshakeState) doFullHandshake() error { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(ckx, msg) + } +- hs.finishedHash.Write(ckx.marshal()) + + preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers) + if err != nil { +@@ -639,7 +636,10 @@ func (hs *serverHandshakeState) doFullHandshake() error { + // to the client's certificate. This allows us to verify that the client is in + // possession of the private key of the certificate. + if len(c.peerCertificates) > 0 { +- msg, err = c.readHandshake() ++ // certificateVerifyMsg is included in the transcript, but not until ++ // after we verify the handshake signature, since the state before ++ // this message was sent is used. ++ msg, err = c.readHandshake(nil) + if err != nil { + return err + } +@@ -674,7 +674,9 @@ func (hs *serverHandshakeState) doFullHandshake() error { + return errors.New("tls: invalid signature by the client certificate: " + err.Error()) + } + +- hs.finishedHash.Write(certVerify.marshal()) ++ if err := transcriptMsg(certVerify, &hs.finishedHash); err != nil { ++ return err ++ } + } + + hs.finishedHash.discardHandshakeBuffer() +@@ -714,7 +716,10 @@ func (hs *serverHandshakeState) readFinished(out []byte) error { + return err + } + +- msg, err := c.readHandshake() ++ // finishedMsg is included in the transcript, but not until after we ++ // check the client version, since the state before this message was ++ // sent is used during verification. ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +@@ -731,7 +736,10 @@ func (hs *serverHandshakeState) readFinished(out []byte) error { + return errors.New("tls: client's Finished message is incorrect") + } + +- hs.finishedHash.Write(clientFinished.marshal()) ++ if err := transcriptMsg(clientFinished, &hs.finishedHash); err != nil { ++ return err ++ } ++ + copy(out, verify) + return nil + } +@@ -765,14 +773,16 @@ func (hs *serverHandshakeState) sendSessionTicket() error { + masterSecret: hs.masterSecret, + certificates: certsFromClient, + } +- var err error +- m.ticket, err = c.encryptTicket(state.marshal()) ++ stateBytes, err := state.marshal() ++ if err != nil { ++ return err ++ } ++ m.ticket, err = c.encryptTicket(stateBytes) + if err != nil { + return err + } + +- hs.finishedHash.Write(m.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(m, &hs.finishedHash); err != nil { + return err + } + +@@ -782,14 +792,13 @@ func (hs *serverHandshakeState) sendSessionTicket() error { + func (hs *serverHandshakeState) sendFinished(out []byte) error { + c := hs.c + +- if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { ++ if err := c.writeChangeCipherRecord(); err != nil { + return err + } + + finished := new(finishedMsg) + finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) +- hs.finishedHash.Write(finished.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(finished, &hs.finishedHash); err != nil { + return err + } + +diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go +index 39b65f6de8..b2e81077be 100644 +--- a/src/crypto/tls/handshake_server_test.go ++++ b/src/crypto/tls/handshake_server_test.go +@@ -30,6 +30,13 @@ func testClientHello(t *testing.T, serverConfig *Config, m handshakeMessage) { + testClientHelloFailure(t, serverConfig, m, "") + } + ++// testFatal is a hack to prevent the compiler from complaining that there is a ++// call to t.Fatal from a non-test goroutine ++func testFatal(t *testing.T, err error) { ++ t.Helper() ++ t.Fatal(err) ++} ++ + func testClientHelloFailure(t *testing.T, serverConfig *Config, m handshakeMessage, expectedSubStr string) { + c, s := localPipe(t) + go func() { +@@ -37,7 +44,9 @@ func testClientHelloFailure(t *testing.T, serverConfig *Config, m handshakeMessa + if ch, ok := m.(*clientHelloMsg); ok { + cli.vers = ch.vers + } +- cli.writeRecord(recordTypeHandshake, m.marshal()) ++ if _, err := cli.writeHandshakeRecord(m, nil); err != nil { ++ testFatal(t, err) ++ } + c.Close() + }() + ctx := context.Background() +@@ -194,7 +203,9 @@ func TestRenegotiationExtension(t *testing.T) { + go func() { + cli := Client(c, testConfig) + cli.vers = clientHello.vers +- cli.writeRecord(recordTypeHandshake, clientHello.marshal()) ++ if _, err := cli.writeHandshakeRecord(clientHello, nil); err != nil { ++ testFatal(t, err) ++ } + + buf := make([]byte, 1024) + n, err := c.Read(buf) +@@ -253,8 +264,10 @@ func TestTLS12OnlyCipherSuites(t *testing.T) { + go func() { + cli := Client(c, testConfig) + cli.vers = clientHello.vers +- cli.writeRecord(recordTypeHandshake, clientHello.marshal()) +- reply, err := cli.readHandshake() ++ if _, err := cli.writeHandshakeRecord(clientHello, nil); err != nil { ++ testFatal(t, err) ++ } ++ reply, err := cli.readHandshake(nil) + c.Close() + if err != nil { + replyChan <- err +@@ -311,8 +324,10 @@ func TestTLSPointFormats(t *testing.T) { + go func() { + cli := Client(c, testConfig) + cli.vers = clientHello.vers +- cli.writeRecord(recordTypeHandshake, clientHello.marshal()) +- reply, err := cli.readHandshake() ++ if _, err := cli.writeHandshakeRecord(clientHello, nil); err != nil { ++ testFatal(t, err) ++ } ++ reply, err := cli.readHandshake(nil) + c.Close() + if err != nil { + replyChan <- err +@@ -1426,7 +1441,9 @@ func TestSNIGivenOnFailure(t *testing.T) { + go func() { + cli := Client(c, testConfig) + cli.vers = clientHello.vers +- cli.writeRecord(recordTypeHandshake, clientHello.marshal()) ++ if _, err := cli.writeHandshakeRecord(clientHello, nil); err != nil { ++ testFatal(t, err) ++ } + c.Close() + }() + conn := Server(s, serverConfig) +diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go +index 03a477f7be..0043e16b31 100644 +--- a/src/crypto/tls/handshake_server_tls13.go ++++ b/src/crypto/tls/handshake_server_tls13.go +@@ -302,7 +302,12 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error { + c.sendAlert(alertInternalError) + return errors.New("tls: internal error: failed to clone hash") + } +- transcript.Write(hs.clientHello.marshalWithoutBinders()) ++ clientHelloBytes, err := hs.clientHello.marshalWithoutBinders() ++ if err != nil { ++ c.sendAlert(alertInternalError) ++ return err ++ } ++ transcript.Write(clientHelloBytes) + pskBinder := hs.suite.finishedHash(binderKey, transcript) + if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) { + c.sendAlert(alertDecryptError) +@@ -393,8 +398,7 @@ func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error { + } + hs.sentDummyCCS = true + +- _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) +- return err ++ return hs.c.writeChangeCipherRecord() + } + + func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error { +@@ -402,7 +406,9 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) + + // The first ClientHello gets double-hashed into the transcript upon a + // HelloRetryRequest. See RFC 8446, Section 4.4.1. +- hs.transcript.Write(hs.clientHello.marshal()) ++ if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil { ++ return err ++ } + chHash := hs.transcript.Sum(nil) + hs.transcript.Reset() + hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) +@@ -418,8 +424,7 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) + selectedGroup: selectedGroup, + } + +- hs.transcript.Write(helloRetryRequest.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(helloRetryRequest, hs.transcript); err != nil { + return err + } + +@@ -427,7 +432,8 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) + return err + } + +- msg, err := c.readHandshake() ++ // clientHelloMsg is not included in the transcript. ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +@@ -518,9 +524,10 @@ func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool { + func (hs *serverHandshakeStateTLS13) sendServerParameters() error { + c := hs.c + +- hs.transcript.Write(hs.clientHello.marshal()) +- hs.transcript.Write(hs.hello.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { ++ if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil { ++ return err ++ } ++ if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil { + return err + } + +@@ -563,8 +570,7 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error { + encryptedExtensions.alpnProtocol = selectedProto + c.clientProtocol = selectedProto + +- hs.transcript.Write(encryptedExtensions.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(encryptedExtensions, hs.transcript); err != nil { + return err + } + +@@ -593,8 +599,7 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error { + certReq.certificateAuthorities = c.config.ClientCAs.Subjects() + } + +- hs.transcript.Write(certReq.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certReq, hs.transcript); err != nil { + return err + } + } +@@ -605,8 +610,7 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error { + certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0 + certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 + +- hs.transcript.Write(certMsg.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certMsg, hs.transcript); err != nil { + return err + } + +@@ -637,8 +641,7 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error { + } + certVerifyMsg.signature = sig + +- hs.transcript.Write(certVerifyMsg.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(certVerifyMsg, hs.transcript); err != nil { + return err + } + +@@ -652,8 +655,7 @@ func (hs *serverHandshakeStateTLS13) sendServerFinished() error { + verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), + } + +- hs.transcript.Write(finished.marshal()) +- if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { ++ if _, err := hs.c.writeHandshakeRecord(finished, hs.transcript); err != nil { + return err + } + +@@ -714,7 +716,9 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { + finishedMsg := &finishedMsg{ + verifyData: hs.clientFinished, + } +- hs.transcript.Write(finishedMsg.marshal()) ++ if err := transcriptMsg(finishedMsg, hs.transcript); err != nil { ++ return err ++ } + + if !hs.shouldSendSessionTickets() { + return nil +@@ -739,8 +743,12 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { + SignedCertificateTimestamps: c.scts, + }, + } +- var err error +- m.label, err = c.encryptTicket(state.marshal()) ++ stateBytes, err := state.marshal() ++ if err != nil { ++ c.sendAlert(alertInternalError) ++ return err ++ } ++ m.label, err = c.encryptTicket(stateBytes) + if err != nil { + return err + } +@@ -759,7 +767,7 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { + // ticket_nonce, which must be unique per connection, is always left at + // zero because we only ever send one ticket per connection. + +- if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { ++ if _, err := c.writeHandshakeRecord(m, nil); err != nil { + return err + } + +@@ -784,7 +792,7 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error { + // If we requested a client certificate, then the client must send a + // certificate message. If it's empty, no CertificateVerify is sent. + +- msg, err := c.readHandshake() ++ msg, err := c.readHandshake(hs.transcript) + if err != nil { + return err + } +@@ -794,7 +802,6 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } +- hs.transcript.Write(certMsg.marshal()) + + if err := c.processCertsFromClient(certMsg.certificate); err != nil { + return err +@@ -808,7 +815,10 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error { + } + + if len(certMsg.certificate.Certificate) != 0 { +- msg, err = c.readHandshake() ++ // certificateVerifyMsg is included in the transcript, but not until ++ // after we verify the handshake signature, since the state before ++ // this message was sent is used. ++ msg, err = c.readHandshake(nil) + if err != nil { + return err + } +@@ -839,7 +849,9 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error { + return errors.New("tls: invalid signature by the client certificate: " + err.Error()) + } + +- hs.transcript.Write(certVerify.marshal()) ++ if err := transcriptMsg(certVerify, hs.transcript); err != nil { ++ return err ++ } + } + + // If we waited until the client certificates to send session tickets, we +@@ -854,7 +866,8 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error { + func (hs *serverHandshakeStateTLS13) readClientFinished() error { + c := hs.c + +- msg, err := c.readHandshake() ++ // finishedMsg is not included in the transcript. ++ msg, err := c.readHandshake(nil) + if err != nil { + return err + } +diff --git a/src/crypto/tls/key_schedule.go b/src/crypto/tls/key_schedule.go +index 314016979a..185137ba06 100644 +--- a/src/crypto/tls/key_schedule.go ++++ b/src/crypto/tls/key_schedule.go +@@ -8,6 +8,7 @@ import ( + "crypto/elliptic" + "crypto/hmac" + "errors" ++ "fmt" + "hash" + "io" + "math/big" +@@ -42,8 +43,24 @@ func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []by + hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(context) + }) ++ hkdfLabelBytes, err := hkdfLabel.Bytes() ++ if err != nil { ++ // Rather than calling BytesOrPanic, we explicitly handle this error, in ++ // order to provide a reasonable error message. It should be basically ++ // impossible for this to panic, and routing errors back through the ++ // tree rooted in this function is quite painful. The labels are fixed ++ // size, and the context is either a fixed-length computed hash, or ++ // parsed from a field which has the same length limitation. As such, an ++ // error here is likely to only be caused during development. ++ // ++ // NOTE: another reasonable approach here might be to return a ++ // randomized slice if we encounter an error, which would break the ++ // connection, but avoid panicking. This would perhaps be safer but ++ // significantly more confusing to users. ++ panic(fmt.Errorf("failed to construct HKDF label: %s", err)) ++ } + out := make([]byte, length) +- n, err := hkdf.Expand(c.hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out) ++ n, err := hkdf.Expand(c.hash.New, secret, hkdfLabelBytes).Read(out) + if err != nil || n != length { + panic("tls: HKDF-Expand-Label invocation failed unexpectedly") + } +diff --git a/src/crypto/tls/ticket.go b/src/crypto/tls/ticket.go +index 6c1d20da20..b82ccd141e 100644 +--- a/src/crypto/tls/ticket.go ++++ b/src/crypto/tls/ticket.go +@@ -32,7 +32,7 @@ type sessionState struct { + usedOldKey bool + } + +-func (m *sessionState) marshal() []byte { ++func (m *sessionState) marshal() ([]byte, error) { + var b cryptobyte.Builder + b.AddUint16(m.vers) + b.AddUint16(m.cipherSuite) +@@ -47,7 +47,7 @@ func (m *sessionState) marshal() []byte { + }) + } + }) +- return b.BytesOrPanic() ++ return b.Bytes() + } + + func (m *sessionState) unmarshal(data []byte) bool { +@@ -86,7 +86,7 @@ type sessionStateTLS13 struct { + certificate Certificate // CertificateEntry certificate_list<0..2^24-1>; + } + +-func (m *sessionStateTLS13) marshal() []byte { ++func (m *sessionStateTLS13) marshal() ([]byte, error) { + var b cryptobyte.Builder + b.AddUint16(VersionTLS13) + b.AddUint8(0) // revision +@@ -96,7 +96,7 @@ func (m *sessionStateTLS13) marshal() []byte { + b.AddBytes(m.resumptionSecret) + }) + marshalCertificate(&b, m.certificate) +- return b.BytesOrPanic() ++ return b.Bytes() + } + + func (m *sessionStateTLS13) unmarshal(data []byte) bool { +-- +2.37.1 + diff --git a/0006-release-branch.go1.19-net-http-update-bundled-golang.patch b/0006-release-branch.go1.19-net-http-update-bundled-golang.patch new file mode 100644 index 0000000..0f588f6 --- /dev/null +++ b/0006-release-branch.go1.19-net-http-update-bundled-golang.patch @@ -0,0 +1,168 @@ +From 5c3e11bd0b5c0a86e5beffcd4339b86a902b21c3 Mon Sep 17 00:00:00 2001 +From: Roland Shoemaker +Date: Mon, 6 Feb 2023 10:03:44 -0800 +Subject: [PATCH] [release-branch.go1.19] net/http: update bundled + golang.org/x/net/http2 + +Disable cmd/internal/moddeps test, since this update includes PRIVATE +track fixes. + +Fixes CVE-2022-41723 +Fixes #58355 +Updates #57855 + +Change-Id: Ie870562a6f6e44e4e8f57db6a0dde1a41a2b090c +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728939 +Reviewed-by: Damien Neil +Reviewed-by: Julie Qiu +Reviewed-by: Tatiana Bradley +Run-TryBot: Roland Shoemaker +Reviewed-on: https://go-review.googlesource.com/c/go/+/468118 +TryBot-Result: Gopher Robot +Run-TryBot: Michael Pratt +Auto-Submit: Michael Pratt +Reviewed-by: Than McIntosh +--- + src/cmd/internal/moddeps/moddeps_test.go | 2 +- + .../golang.org/x/net/http2/hpack/hpack.go | 79 ++++++++++++------- + 2 files changed, 50 insertions(+), 31 deletions(-) + +diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go +index e96edf1f30..1fc9f708bd 100644 +--- a/src/cmd/internal/moddeps/moddeps_test.go ++++ b/src/cmd/internal/moddeps/moddeps_test.go +@@ -33,7 +33,7 @@ import ( + // See issues 36852, 41409, and 43687. + // (Also see golang.org/issue/27348.) + func TestAllDependencies(t *testing.T) { +- t.Skip("TODO(#57009): 1.19.4 contains unreleased changes from vendored modules") ++ t.Skip("TODO(#58355): 1.19.4 contains unreleased changes from vendored modules") + + goBin := testenv.GoToolPath(t) + +diff --git a/src/vendor/golang.org/x/net/http2/hpack/hpack.go b/src/vendor/golang.org/x/net/http2/hpack/hpack.go +index 85f18a2b0a..02e80e30a4 100644 +--- a/src/vendor/golang.org/x/net/http2/hpack/hpack.go ++++ b/src/vendor/golang.org/x/net/http2/hpack/hpack.go +@@ -359,6 +359,7 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error { + + var hf HeaderField + wantStr := d.emitEnabled || it.indexed() ++ var undecodedName undecodedString + if nameIdx > 0 { + ihf, ok := d.at(nameIdx) + if !ok { +@@ -366,15 +367,27 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error { + } + hf.Name = ihf.Name + } else { +- hf.Name, buf, err = d.readString(buf, wantStr) ++ undecodedName, buf, err = d.readString(buf) + if err != nil { + return err + } + } +- hf.Value, buf, err = d.readString(buf, wantStr) ++ undecodedValue, buf, err := d.readString(buf) + if err != nil { + return err + } ++ if wantStr { ++ if nameIdx <= 0 { ++ hf.Name, err = d.decodeString(undecodedName) ++ if err != nil { ++ return err ++ } ++ } ++ hf.Value, err = d.decodeString(undecodedValue) ++ if err != nil { ++ return err ++ } ++ } + d.buf = buf + if it.indexed() { + d.dynTab.add(hf) +@@ -459,46 +472,52 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) { + return 0, origP, errNeedMore + } + +-// readString decodes an hpack string from p. ++// readString reads an hpack string from p. + // +-// wantStr is whether s will be used. If false, decompression and +-// []byte->string garbage are skipped if s will be ignored +-// anyway. This does mean that huffman decoding errors for non-indexed +-// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server +-// is returning an error anyway, and because they're not indexed, the error +-// won't affect the decoding state. +-func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) { ++// It returns a reference to the encoded string data to permit deferring decode costs ++// until after the caller verifies all data is present. ++func (d *Decoder) readString(p []byte) (u undecodedString, remain []byte, err error) { + if len(p) == 0 { +- return "", p, errNeedMore ++ return u, p, errNeedMore + } + isHuff := p[0]&128 != 0 + strLen, p, err := readVarInt(7, p) + if err != nil { +- return "", p, err ++ return u, p, err + } + if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) { +- return "", nil, ErrStringLength ++ // Returning an error here means Huffman decoding errors ++ // for non-indexed strings past the maximum string length ++ // are ignored, but the server is returning an error anyway ++ // and because the string is not indexed the error will not ++ // affect the decoding state. ++ return u, nil, ErrStringLength + } + if uint64(len(p)) < strLen { +- return "", p, errNeedMore +- } +- if !isHuff { +- if wantStr { +- s = string(p[:strLen]) +- } +- return s, p[strLen:], nil ++ return u, p, errNeedMore + } ++ u.isHuff = isHuff ++ u.b = p[:strLen] ++ return u, p[strLen:], nil ++} + +- if wantStr { +- buf := bufPool.Get().(*bytes.Buffer) +- buf.Reset() // don't trust others +- defer bufPool.Put(buf) +- if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil { +- buf.Reset() +- return "", nil, err +- } ++type undecodedString struct { ++ isHuff bool ++ b []byte ++} ++ ++func (d *Decoder) decodeString(u undecodedString) (string, error) { ++ if !u.isHuff { ++ return string(u.b), nil ++ } ++ buf := bufPool.Get().(*bytes.Buffer) ++ buf.Reset() // don't trust others ++ var s string ++ err := huffmanDecode(buf, d.maxStrLen, u.b) ++ if err == nil { + s = buf.String() +- buf.Reset() // be nice to GC + } +- return s, p[strLen:], nil ++ buf.Reset() // be nice to GC ++ bufPool.Put(buf) ++ return s, err + } +-- +2.37.1 + diff --git a/0007-release-branch.go1.19-crypto-internal-nistec-reduce-.patch b/0007-release-branch.go1.19-crypto-internal-nistec-reduce-.patch new file mode 100644 index 0000000..cbdb220 --- /dev/null +++ b/0007-release-branch.go1.19-crypto-internal-nistec-reduce-.patch @@ -0,0 +1,206 @@ +From 639b67ed114151c0d786aa26e7faeab942400703 Mon Sep 17 00:00:00 2001 +From: Filippo Valsorda +Date: Mon, 13 Feb 2023 15:16:27 +0100 +Subject: [PATCH] [release-branch.go1.19] crypto/internal/nistec: reduce P-256 + scalar + +Unlike the rest of nistec, the P-256 assembly doesn't use complete +addition formulas, meaning that p256PointAdd[Affine]Asm won't return the +correct value if the two inputs are equal. + +This was (undocumentedly) ignored in the scalar multiplication loops +because as long as the input point is not the identity and the scalar is +lower than the order of the group, the addition inputs can't be the same. + +As part of the math/big rewrite, we went however from always reducing +the scalar to only checking its length, under the incorrect assumption +that the scalar multiplication loop didn't require reduction. + +Added a reduction, and while at it added it in P256OrdInverse, too, to +enforce a universal reduction invariant on p256OrdElement values. + +Note that if the input point is the infinity, the code currently still +relies on undefined behavior, but that's easily tested to behave +acceptably, and will be addressed in a future CL. + +Updates #58647 +Fixes #58719 +Fixes CVE-2023-24532 + +(Filed with the "safe APIs like complete addition formulas are good" dept.) + +Change-Id: I7b2c75238440e6852be2710fad66ff1fdc4e2b24 +Reviewed-on: https://go-review.googlesource.com/c/go/+/471255 +TryBot-Result: Gopher Robot +Reviewed-by: Roland Shoemaker +Run-TryBot: Filippo Valsorda +Auto-Submit: Filippo Valsorda +Reviewed-by: Damien Neil +(cherry picked from commit 203e59ad41bd288e1d92b6f617c2f55e70d3c8e3) +Reviewed-on: https://go-review.googlesource.com/c/go/+/471696 +Auto-Submit: Dmitri Shuralyov +Reviewed-by: Dmitri Shuralyov +Run-TryBot: Roland Shoemaker +Reviewed-by: Filippo Valsorda +--- + src/crypto/internal/nistec/nistec_test.go | 81 +++++++++++++++++++ + src/crypto/internal/nistec/p256_asm.go | 17 ++++ + src/crypto/internal/nistec/p256_asm_ordinv.go | 1 + + 3 files changed, 99 insertions(+) + +diff --git a/src/crypto/internal/nistec/nistec_test.go b/src/crypto/internal/nistec/nistec_test.go +index 1903f19af3..1cedebc8ac 100644 +--- a/src/crypto/internal/nistec/nistec_test.go ++++ b/src/crypto/internal/nistec/nistec_test.go +@@ -8,6 +8,7 @@ import ( + "bytes" + "crypto/elliptic" + "crypto/internal/nistec" ++ "fmt" + "math/big" + "math/rand" + "os" +@@ -166,6 +167,86 @@ func testEquivalents[P nistPoint[P]](t *testing.T, newPoint, newGenerator func() + } + } + ++func TestScalarMult(t *testing.T) { ++ t.Run("P224", func(t *testing.T) { ++ testScalarMult(t, nistec.NewP224Point, nistec.NewP224Generator, elliptic.P224()) ++ }) ++ t.Run("P256", func(t *testing.T) { ++ testScalarMult(t, nistec.NewP256Point, nistec.NewP256Generator, elliptic.P256()) ++ }) ++ t.Run("P384", func(t *testing.T) { ++ testScalarMult(t, nistec.NewP384Point, nistec.NewP384Generator, elliptic.P384()) ++ }) ++ t.Run("P521", func(t *testing.T) { ++ testScalarMult(t, nistec.NewP521Point, nistec.NewP521Generator, elliptic.P521()) ++ }) ++} ++ ++func testScalarMult[P nistPoint[P]](t *testing.T, newPoint func() P, newGenerator func() P, c elliptic.Curve) { ++ G := newGenerator() ++ checkScalar := func(t *testing.T, scalar []byte) { ++ p1, err := newPoint().ScalarBaseMult(scalar) ++ fatalIfErr(t, err) ++ p2, err := newPoint().ScalarMult(G, scalar) ++ fatalIfErr(t, err) ++ if !bytes.Equal(p1.Bytes(), p2.Bytes()) { ++ t.Error("[k]G != ScalarBaseMult(k)") ++ } ++ ++ d := new(big.Int).SetBytes(scalar) ++ d.Sub(c.Params().N, d) ++ d.Mod(d, c.Params().N) ++ g1, err := newPoint().ScalarBaseMult(d.FillBytes(make([]byte, len(scalar)))) ++ fatalIfErr(t, err) ++ g1.Add(g1, p1) ++ if !bytes.Equal(g1.Bytes(), newPoint().Bytes()) { ++ t.Error("[N - k]G + [k]G != ∞") ++ } ++ } ++ ++ byteLen := len(c.Params().N.Bytes()) ++ bitLen := c.Params().N.BitLen() ++ t.Run("0", func(t *testing.T) { checkScalar(t, make([]byte, byteLen)) }) ++ t.Run("1", func(t *testing.T) { ++ checkScalar(t, big.NewInt(1).FillBytes(make([]byte, byteLen))) ++ }) ++ t.Run("N-1", func(t *testing.T) { ++ checkScalar(t, new(big.Int).Sub(c.Params().N, big.NewInt(1)).Bytes()) ++ }) ++ t.Run("N", func(t *testing.T) { checkScalar(t, c.Params().N.Bytes()) }) ++ t.Run("N+1", func(t *testing.T) { ++ checkScalar(t, new(big.Int).Add(c.Params().N, big.NewInt(1)).Bytes()) ++ }) ++ t.Run("all1s", func(t *testing.T) { ++ s := new(big.Int).Lsh(big.NewInt(1), uint(bitLen)) ++ s.Sub(s, big.NewInt(1)) ++ checkScalar(t, s.Bytes()) ++ }) ++ if testing.Short() { ++ return ++ } ++ for i := 0; i < bitLen; i++ { ++ t.Run(fmt.Sprintf("1<<%d", i), func(t *testing.T) { ++ s := new(big.Int).Lsh(big.NewInt(1), uint(i)) ++ checkScalar(t, s.FillBytes(make([]byte, byteLen))) ++ }) ++ } ++ // Test N+1...N+32 since they risk overlapping with precomputed table values ++ // in the final additions. ++ for i := int64(2); i <= 32; i++ { ++ t.Run(fmt.Sprintf("N+%d", i), func(t *testing.T) { ++ checkScalar(t, new(big.Int).Add(c.Params().N, big.NewInt(i)).Bytes()) ++ }) ++ } ++} ++ ++func fatalIfErr(t *testing.T, err error) { ++ t.Helper() ++ if err != nil { ++ t.Fatal(err) ++ } ++} ++ + func BenchmarkScalarMult(b *testing.B) { + b.Run("P224", func(b *testing.B) { + benchmarkScalarMult(b, nistec.NewP224Generator(), 28) +diff --git a/src/crypto/internal/nistec/p256_asm.go b/src/crypto/internal/nistec/p256_asm.go +index bc443ba323..14713b0406 100644 +--- a/src/crypto/internal/nistec/p256_asm.go ++++ b/src/crypto/internal/nistec/p256_asm.go +@@ -365,6 +365,21 @@ func p256PointDoubleAsm(res, in *P256Point) + // Montgomery domain (with R 2²⁵⁶) as four uint64 limbs in little-endian order. + type p256OrdElement [4]uint64 + ++// p256OrdReduce ensures s is in the range [0, ord(G)-1]. ++func p256OrdReduce(s *p256OrdElement) { ++ // Since 2 * ord(G) > 2²⁵⁶, we can just conditionally subtract ord(G), ++ // keeping the result if it doesn't underflow. ++ t0, b := bits.Sub64(s[0], 0xf3b9cac2fc632551, 0) ++ t1, b := bits.Sub64(s[1], 0xbce6faada7179e84, b) ++ t2, b := bits.Sub64(s[2], 0xffffffffffffffff, b) ++ t3, b := bits.Sub64(s[3], 0xffffffff00000000, b) ++ tMask := b - 1 // zero if subtraction underflowed ++ s[0] ^= (t0 ^ s[0]) & tMask ++ s[1] ^= (t1 ^ s[1]) & tMask ++ s[2] ^= (t2 ^ s[2]) & tMask ++ s[3] ^= (t3 ^ s[3]) & tMask ++} ++ + // Add sets q = p1 + p2, and returns q. The points may overlap. + func (q *P256Point) Add(r1, r2 *P256Point) *P256Point { + var sum, double P256Point +@@ -394,6 +409,7 @@ func (r *P256Point) ScalarBaseMult(scalar []byte) (*P256Point, error) { + } + scalarReversed := new(p256OrdElement) + p256OrdBigToLittle(scalarReversed, (*[32]byte)(scalar)) ++ p256OrdReduce(scalarReversed) + + r.p256BaseMult(scalarReversed) + return r, nil +@@ -408,6 +424,7 @@ func (r *P256Point) ScalarMult(q *P256Point, scalar []byte) (*P256Point, error) + } + scalarReversed := new(p256OrdElement) + p256OrdBigToLittle(scalarReversed, (*[32]byte)(scalar)) ++ p256OrdReduce(scalarReversed) + + r.Set(q).p256ScalarMult(scalarReversed) + return r, nil +diff --git a/src/crypto/internal/nistec/p256_asm_ordinv.go b/src/crypto/internal/nistec/p256_asm_ordinv.go +index 86a7a230bd..1274fb7fd3 100644 +--- a/src/crypto/internal/nistec/p256_asm_ordinv.go ++++ b/src/crypto/internal/nistec/p256_asm_ordinv.go +@@ -25,6 +25,7 @@ func P256OrdInverse(k []byte) ([]byte, error) { + + x := new(p256OrdElement) + p256OrdBigToLittle(x, (*[32]byte)(k)) ++ p256OrdReduce(x) + + // Inversion is implemented as exponentiation by n - 2, per Fermat's little theorem. + // +-- +2.37.1 + diff --git a/0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch b/0008-release-branch.go1.19-net-textproto-avoid-overpredic.patch similarity index 99% rename from 0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch rename to 0008-release-branch.go1.19-net-textproto-avoid-overpredic.patch index d963083..1469715 100644 --- a/0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch +++ b/0008-release-branch.go1.19-net-textproto-avoid-overpredic.patch @@ -184,5 +184,5 @@ index d11d40f1cf..f3c372ce03 100644 in string inCode int -- -2.33.0 +2.37.1 diff --git a/0009-release-branch.go1.19-mime-multipart-avoid-excessive.patch b/0009-release-branch.go1.19-mime-multipart-avoid-excessive.patch new file mode 100644 index 0000000..715578d --- /dev/null +++ b/0009-release-branch.go1.19-mime-multipart-avoid-excessive.patch @@ -0,0 +1,133 @@ +From ef41a4e2face45e580c5836eaebd51629fc23f15 Mon Sep 17 00:00:00 2001 +From: Damien Neil +Date: Thu, 16 Mar 2023 14:18:04 -0700 +Subject: [PATCH] [release-branch.go1.19] mime/multipart: avoid excessive copy + buffer allocations in ReadForm + +When copying form data to disk with io.Copy, +allocate only one copy buffer and reuse it rather than +creating two buffers per file (one from io.multiReader.WriteTo, +and a second one from os.File.ReadFrom). + +Thanks to Jakob Ackermann (@das7pad) for reporting this issue. + +For CVE-2023-24536 +For #59153 +For #59269 + +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802453 +Run-TryBot: Damien Neil +Reviewed-by: Julie Qiu +Reviewed-by: Roland Shoemaker +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802395 +Run-TryBot: Roland Shoemaker +Reviewed-by: Damien Neil +Change-Id: Ie405470c92abffed3356913b37d813e982c96c8b +Reviewed-on: https://go-review.googlesource.com/c/go/+/481983 +Run-TryBot: Michael Knyszek +TryBot-Result: Gopher Robot +Auto-Submit: Michael Knyszek +Reviewed-by: Matthew Dempsky +--- + src/mime/multipart/formdata.go | 15 +++++++-- + src/mime/multipart/formdata_test.go | 49 +++++++++++++++++++++++++++++ + 2 files changed, 61 insertions(+), 3 deletions(-) + +diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go +index a7d4ca97f0..975dcb6b26 100644 +--- a/src/mime/multipart/formdata.go ++++ b/src/mime/multipart/formdata.go +@@ -84,6 +84,7 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + maxMemoryBytes = math.MaxInt64 + } + } ++ var copyBuf []byte + for { + p, err := r.nextPart(false, maxMemoryBytes) + if err == io.EOF { +@@ -147,14 +148,22 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + } + } + numDiskFiles++ +- size, err := io.Copy(file, io.MultiReader(&b, p)) ++ if _, err := file.Write(b.Bytes()); err != nil { ++ return nil, err ++ } ++ if copyBuf == nil { ++ copyBuf = make([]byte, 32*1024) // same buffer size as io.Copy uses ++ } ++ // os.File.ReadFrom will allocate its own copy buffer if we let io.Copy use it. ++ type writerOnly struct{ io.Writer } ++ remainingSize, err := io.CopyBuffer(writerOnly{file}, p, copyBuf) + if err != nil { + return nil, err + } + fh.tmpfile = file.Name() +- fh.Size = size ++ fh.Size = int64(b.Len()) + remainingSize + fh.tmpoff = fileOff +- fileOff += size ++ fileOff += fh.Size + if !combineFiles { + if err := file.Close(); err != nil { + return nil, err +diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go +index 5cded7170c..f5b56083b2 100644 +--- a/src/mime/multipart/formdata_test.go ++++ b/src/mime/multipart/formdata_test.go +@@ -368,3 +368,52 @@ func testReadFormManyFiles(t *testing.T, distinct bool) { + t.Fatalf("temp dir contains %v files; want 0", len(names)) + } + } ++ ++func BenchmarkReadForm(b *testing.B) { ++ for _, test := range []struct { ++ name string ++ form func(fw *Writer, count int) ++ }{{ ++ name: "fields", ++ form: func(fw *Writer, count int) { ++ for i := 0; i < count; i++ { ++ w, _ := fw.CreateFormField(fmt.Sprintf("field%v", i)) ++ fmt.Fprintf(w, "value %v", i) ++ } ++ }, ++ }, { ++ name: "files", ++ form: func(fw *Writer, count int) { ++ for i := 0; i < count; i++ { ++ w, _ := fw.CreateFormFile(fmt.Sprintf("field%v", i), fmt.Sprintf("file%v", i)) ++ fmt.Fprintf(w, "value %v", i) ++ } ++ }, ++ }} { ++ b.Run(test.name, func(b *testing.B) { ++ for _, maxMemory := range []int64{ ++ 0, ++ 1 << 20, ++ } { ++ var buf bytes.Buffer ++ fw := NewWriter(&buf) ++ test.form(fw, 10) ++ if err := fw.Close(); err != nil { ++ b.Fatal(err) ++ } ++ b.Run(fmt.Sprintf("maxMemory=%v", maxMemory), func(b *testing.B) { ++ b.ReportAllocs() ++ for i := 0; i < b.N; i++ { ++ fr := NewReader(bytes.NewReader(buf.Bytes()), fw.Boundary()) ++ form, err := fr.ReadForm(maxMemory) ++ if err != nil { ++ b.Fatal(err) ++ } ++ form.RemoveAll() ++ } ++ ++ }) ++ } ++ }) ++ } ++} +-- +2.37.1 + diff --git a/0010-release-branch.go1.19-net-textproto-mime-multipart-i.patch b/0010-release-branch.go1.19-net-textproto-mime-multipart-i.patch new file mode 100644 index 0000000..e48292f --- /dev/null +++ b/0010-release-branch.go1.19-net-textproto-mime-multipart-i.patch @@ -0,0 +1,183 @@ +From 7a359a651c7ebdb29e0a1c03102fce793e9f58f0 Mon Sep 17 00:00:00 2001 +From: Damien Neil +Date: Thu, 16 Mar 2023 16:56:12 -0700 +Subject: [PATCH] [release-branch.go1.19] net/textproto, mime/multipart: + improve accounting of non-file data + +For requests containing large numbers of small parts, +memory consumption of a parsed form could be about 250% +over the estimated size. + +When considering the size of parsed forms, account for the size of +FileHeader structs and increase the estimate of memory consumed by +map entries. + +Thanks to Jakob Ackermann (@das7pad) for reporting this issue. + +For CVE-2023-24536 +For #59153 +For #59269 + +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802454 +Run-TryBot: Damien Neil +Reviewed-by: Roland Shoemaker +Reviewed-by: Julie Qiu +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802396 +Run-TryBot: Roland Shoemaker +Reviewed-by: Damien Neil +Change-Id: I31bc50e9346b4eee6fbe51a18c3c57230cc066db +Reviewed-on: https://go-review.googlesource.com/c/go/+/481984 +Reviewed-by: Matthew Dempsky +Auto-Submit: Michael Knyszek +TryBot-Result: Gopher Robot +Run-TryBot: Michael Knyszek +--- + src/mime/multipart/formdata.go | 9 +++-- + src/mime/multipart/formdata_test.go | 55 ++++++++++++----------------- + src/net/textproto/reader.go | 8 ++++- + 3 files changed, 37 insertions(+), 35 deletions(-) + +diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go +index 975dcb6b26..3f6ff697ca 100644 +--- a/src/mime/multipart/formdata.go ++++ b/src/mime/multipart/formdata.go +@@ -103,8 +103,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + // Multiple values for the same key (one map entry, longer slice) are cheaper + // than the same number of values for different keys (many map entries), but + // using a consistent per-value cost for overhead is simpler. ++ const mapEntryOverhead = 200 + maxMemoryBytes -= int64(len(name)) +- maxMemoryBytes -= 100 // map overhead ++ maxMemoryBytes -= mapEntryOverhead + if maxMemoryBytes < 0 { + // We can't actually take this path, since nextPart would already have + // rejected the MIME headers for being too large. Check anyway. +@@ -128,7 +129,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + } + + // file, store in memory or on disk ++ const fileHeaderSize = 100 + maxMemoryBytes -= mimeHeaderSize(p.Header) ++ maxMemoryBytes -= mapEntryOverhead ++ maxMemoryBytes -= fileHeaderSize + if maxMemoryBytes < 0 { + return nil, ErrMessageTooLarge + } +@@ -183,9 +187,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + } + + func mimeHeaderSize(h textproto.MIMEHeader) (size int64) { ++ size = 400 + for k, vs := range h { + size += int64(len(k)) +- size += 100 // map entry overhead ++ size += 200 // map entry overhead + for _, v := range vs { + size += int64(len(v)) + } +diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go +index f5b56083b2..8ed26e0c34 100644 +--- a/src/mime/multipart/formdata_test.go ++++ b/src/mime/multipart/formdata_test.go +@@ -192,10 +192,10 @@ func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) { + // TestReadForm_NonFileMaxMemory asserts that the ReadForm maxMemory limit is applied + // while processing non-file form data as well as file form data. + func TestReadForm_NonFileMaxMemory(t *testing.T) { +- n := 10<<20 + 25 + if testing.Short() { +- n = 10<<10 + 25 ++ t.Skip("skipping in -short mode") + } ++ n := 10 << 20 + largeTextValue := strings.Repeat("1", n) + message := `--MyBoundary + Content-Disposition: form-data; name="largetext" +@@ -203,38 +203,29 @@ Content-Disposition: form-data; name="largetext" + ` + largeTextValue + ` + --MyBoundary-- + ` +- + testBody := strings.ReplaceAll(message, "\n", "\r\n") +- testCases := []struct { +- name string +- maxMemory int64 +- err error +- }{ +- {"smaller", 50 + int64(len("largetext")) + 100, nil}, +- {"exact-fit", 25 + int64(len("largetext")) + 100, nil}, +- {"too-large", 0, ErrMessageTooLarge}, +- } +- for _, tc := range testCases { +- t.Run(tc.name, func(t *testing.T) { +- if tc.maxMemory == 0 && testing.Short() { +- t.Skip("skipping in -short mode") +- } +- b := strings.NewReader(testBody) +- r := NewReader(b, boundary) +- f, err := r.ReadForm(tc.maxMemory) +- if err == nil { +- defer f.RemoveAll() +- } +- if tc.err != err { +- t.Fatalf("ReadForm error - got: %v; expected: %v", err, tc.err) +- } +- if err == nil { +- if g := f.Value["largetext"][0]; g != largeTextValue { +- t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue)) +- } +- } +- }) ++ // Try parsing the form with increasing maxMemory values. ++ // Changes in how we account for non-file form data may cause the exact point ++ // where we change from rejecting the form as too large to accepting it to vary, ++ // but we should see both successes and failures. ++ const failWhenMaxMemoryLessThan = 128 ++ for maxMemory := int64(0); maxMemory < failWhenMaxMemoryLessThan*2; maxMemory += 16 { ++ b := strings.NewReader(testBody) ++ r := NewReader(b, boundary) ++ f, err := r.ReadForm(maxMemory) ++ if err != nil { ++ continue ++ } ++ if g := f.Value["largetext"][0]; g != largeTextValue { ++ t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue)) ++ } ++ f.RemoveAll() ++ if maxMemory < failWhenMaxMemoryLessThan { ++ t.Errorf("ReadForm(%v): no error, expect to hit memory limit when maxMemory < %v", maxMemory, failWhenMaxMemoryLessThan) ++ } ++ return + } ++ t.Errorf("ReadForm(x) failed for x < 1024, expect success") + } + + // TestReadForm_MetadataTooLarge verifies that we account for the size of field names, +diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go +index 9a21777df8..c1284fde25 100644 +--- a/src/net/textproto/reader.go ++++ b/src/net/textproto/reader.go +@@ -503,6 +503,12 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { + + m := make(MIMEHeader, hint) + ++ // Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry. ++ // Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large ++ // MIMEHeaders average about 200 bytes per entry. ++ lim -= 400 ++ const mapEntryOverhead = 200 ++ + // The first line cannot start with a leading space. + if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') { + line, err := r.readLineSlice() +@@ -538,7 +544,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { + vv := m[key] + if vv == nil { + lim -= int64(len(key)) +- lim -= 100 // map entry overhead ++ lim -= mapEntryOverhead + } + lim -= int64(len(value)) + if lim < 0 { +-- +2.37.1 + diff --git a/0011-release-branch.go1.19-mime-multipart-limit-parsed-mi.patch b/0011-release-branch.go1.19-mime-multipart-limit-parsed-mi.patch new file mode 100644 index 0000000..a52e793 --- /dev/null +++ b/0011-release-branch.go1.19-mime-multipart-limit-parsed-mi.patch @@ -0,0 +1,344 @@ +From 7917b5f31204528ea72e0629f0b7d52b35b27538 Mon Sep 17 00:00:00 2001 +From: Damien Neil +Date: Mon, 20 Mar 2023 10:43:19 -0700 +Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit parsed mime + message sizes + +The parsed forms of MIME headers and multipart forms can consume +substantially more memory than the size of the input data. +A malicious input containing a very large number of headers or +form parts can cause excessively large memory allocations. + +Set limits on the size of MIME data: + +Reader.NextPart and Reader.NextRawPart limit the the number +of headers in a part to 10000. + +Reader.ReadForm limits the total number of headers in all +FileHeaders to 10000. + +Both of these limits may be set with with +GODEBUG=multipartmaxheaders=. + +Reader.ReadForm limits the number of parts in a form to 1000. +This limit may be set with GODEBUG=multipartmaxparts=. + +Thanks for Jakob Ackermann (@das7pad) for reporting this issue. + +For CVE-2023-24536 +For #59153 +For #59269 + +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802455 +Run-TryBot: Damien Neil +Reviewed-by: Roland Shoemaker +Reviewed-by: Julie Qiu +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1801087 +Reviewed-by: Damien Neil +Run-TryBot: Roland Shoemaker +Change-Id: If134890d75f0d95c681d67234daf191ba08e6424 +Reviewed-on: https://go-review.googlesource.com/c/go/+/481985 +Run-TryBot: Michael Knyszek +Auto-Submit: Michael Knyszek +TryBot-Result: Gopher Robot +Reviewed-by: Matthew Dempsky +--- + src/mime/multipart/formdata.go | 19 ++++++++- + src/mime/multipart/formdata_test.go | 61 ++++++++++++++++++++++++++++ + src/mime/multipart/multipart.go | 31 ++++++++++---- + src/mime/multipart/readmimeheader.go | 2 +- + src/net/textproto/reader.go | 19 +++++---- + 5 files changed, 115 insertions(+), 17 deletions(-) + +diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go +index 3f6ff697ca..4f26aab2cf 100644 +--- a/src/mime/multipart/formdata.go ++++ b/src/mime/multipart/formdata.go +@@ -12,6 +12,7 @@ import ( + "math" + "net/textproto" + "os" ++ "strconv" + ) + + // ErrMessageTooLarge is returned by ReadForm if the message form +@@ -41,6 +42,15 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + numDiskFiles := 0 + multipartFiles := godebug.Get("multipartfiles") + combineFiles := multipartFiles != "distinct" ++ maxParts := 1000 ++ multipartMaxParts := godebug.Get("multipartmaxparts") ++ if multipartMaxParts != "" { ++ if v, err := strconv.Atoi(multipartMaxParts); err == nil && v >= 0 { ++ maxParts = v ++ } ++ } ++ maxHeaders := maxMIMEHeaders() ++ + defer func() { + if file != nil { + if cerr := file.Close(); err == nil { +@@ -86,13 +96,17 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + } + var copyBuf []byte + for { +- p, err := r.nextPart(false, maxMemoryBytes) ++ p, err := r.nextPart(false, maxMemoryBytes, maxHeaders) + if err == io.EOF { + break + } + if err != nil { + return nil, err + } ++ if maxParts <= 0 { ++ return nil, ErrMessageTooLarge ++ } ++ maxParts-- + + name := p.FormName() + if name == "" { +@@ -136,6 +150,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { + if maxMemoryBytes < 0 { + return nil, ErrMessageTooLarge + } ++ for _, v := range p.Header { ++ maxHeaders -= int64(len(v)) ++ } + fh := &FileHeader{ + Filename: filename, + Header: p.Header, +diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go +index 8ed26e0c34..c78eeb7a12 100644 +--- a/src/mime/multipart/formdata_test.go ++++ b/src/mime/multipart/formdata_test.go +@@ -360,6 +360,67 @@ func testReadFormManyFiles(t *testing.T, distinct bool) { + } + } + ++func TestReadFormLimits(t *testing.T) { ++ for _, test := range []struct { ++ values int ++ files int ++ extraKeysPerFile int ++ wantErr error ++ godebug string ++ }{ ++ {values: 1000}, ++ {values: 1001, wantErr: ErrMessageTooLarge}, ++ {values: 500, files: 500}, ++ {values: 501, files: 500, wantErr: ErrMessageTooLarge}, ++ {files: 1000}, ++ {files: 1001, wantErr: ErrMessageTooLarge}, ++ {files: 1, extraKeysPerFile: 9998}, // plus Content-Disposition and Content-Type ++ {files: 1, extraKeysPerFile: 10000, wantErr: ErrMessageTooLarge}, ++ {godebug: "multipartmaxparts=100", values: 100}, ++ {godebug: "multipartmaxparts=100", values: 101, wantErr: ErrMessageTooLarge}, ++ {godebug: "multipartmaxheaders=100", files: 2, extraKeysPerFile: 48}, ++ {godebug: "multipartmaxheaders=100", files: 2, extraKeysPerFile: 50, wantErr: ErrMessageTooLarge}, ++ } { ++ name := fmt.Sprintf("values=%v/files=%v/extraKeysPerFile=%v", test.values, test.files, test.extraKeysPerFile) ++ if test.godebug != "" { ++ name += fmt.Sprintf("/godebug=%v", test.godebug) ++ } ++ t.Run(name, func(t *testing.T) { ++ if test.godebug != "" { ++ t.Setenv("GODEBUG", test.godebug) ++ } ++ var buf bytes.Buffer ++ fw := NewWriter(&buf) ++ for i := 0; i < test.values; i++ { ++ w, _ := fw.CreateFormField(fmt.Sprintf("field%v", i)) ++ fmt.Fprintf(w, "value %v", i) ++ } ++ for i := 0; i < test.files; i++ { ++ h := make(textproto.MIMEHeader) ++ h.Set("Content-Disposition", ++ fmt.Sprintf(`form-data; name="file%v"; filename="file%v"`, i, i)) ++ h.Set("Content-Type", "application/octet-stream") ++ for j := 0; j < test.extraKeysPerFile; j++ { ++ h.Set(fmt.Sprintf("k%v", j), "v") ++ } ++ w, _ := fw.CreatePart(h) ++ fmt.Fprintf(w, "value %v", i) ++ } ++ if err := fw.Close(); err != nil { ++ t.Fatal(err) ++ } ++ fr := NewReader(bytes.NewReader(buf.Bytes()), fw.Boundary()) ++ form, err := fr.ReadForm(1 << 10) ++ if err == nil { ++ defer form.RemoveAll() ++ } ++ if err != test.wantErr { ++ t.Errorf("ReadForm = %v, want %v", err, test.wantErr) ++ } ++ }) ++ } ++} ++ + func BenchmarkReadForm(b *testing.B) { + for _, test := range []struct { + name string +diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go +index fc50d35196..fab676a70a 100644 +--- a/src/mime/multipart/multipart.go ++++ b/src/mime/multipart/multipart.go +@@ -16,11 +16,13 @@ import ( + "bufio" + "bytes" + "fmt" ++ "internal/godebug" + "io" + "mime" + "mime/quotedprintable" + "net/textproto" + "path/filepath" ++ "strconv" + "strings" + ) + +@@ -128,12 +130,12 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) { + return n, r.err + } + +-func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { ++func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) { + bp := &Part{ + Header: make(map[string][]string), + mr: mr, + } +- if err := bp.populateHeaders(maxMIMEHeaderSize); err != nil { ++ if err := bp.populateHeaders(maxMIMEHeaderSize, maxMIMEHeaders); err != nil { + return nil, err + } + bp.r = partReader{bp} +@@ -149,9 +151,9 @@ func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { + return bp, nil + } + +-func (p *Part) populateHeaders(maxMIMEHeaderSize int64) error { ++func (p *Part) populateHeaders(maxMIMEHeaderSize, maxMIMEHeaders int64) error { + r := textproto.NewReader(p.mr.bufReader) +- header, err := readMIMEHeader(r, maxMIMEHeaderSize) ++ header, err := readMIMEHeader(r, maxMIMEHeaderSize, maxMIMEHeaders) + if err == nil { + p.Header = header + } +@@ -330,6 +332,19 @@ type Reader struct { + // including header keys, values, and map overhead. + const maxMIMEHeaderSize = 10 << 20 + ++func maxMIMEHeaders() int64 { ++ // multipartMaxHeaders is the maximum number of header entries NextPart will return, ++ // as well as the maximum combined total of header entries Reader.ReadForm will return ++ // in FileHeaders. ++ multipartMaxHeaders := godebug.Get("multipartmaxheaders") ++ if multipartMaxHeaders != "" { ++ if v, err := strconv.ParseInt(multipartMaxHeaders, 10, 64); err == nil && v >= 0 { ++ return v ++ } ++ } ++ return 10000 ++} ++ + // NextPart returns the next part in the multipart or an error. + // When there are no more parts, the error io.EOF is returned. + // +@@ -337,7 +352,7 @@ const maxMIMEHeaderSize = 10 << 20 + // has a value of "quoted-printable", that header is instead + // hidden and the body is transparently decoded during Read calls. + func (r *Reader) NextPart() (*Part, error) { +- return r.nextPart(false, maxMIMEHeaderSize) ++ return r.nextPart(false, maxMIMEHeaderSize, maxMIMEHeaders()) + } + + // NextRawPart returns the next part in the multipart or an error. +@@ -346,10 +361,10 @@ func (r *Reader) NextPart() (*Part, error) { + // Unlike NextPart, it does not have special handling for + // "Content-Transfer-Encoding: quoted-printable". + func (r *Reader) NextRawPart() (*Part, error) { +- return r.nextPart(true, maxMIMEHeaderSize) ++ return r.nextPart(true, maxMIMEHeaderSize, maxMIMEHeaders()) + } + +-func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { ++func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) { + if r.currentPart != nil { + r.currentPart.Close() + } +@@ -374,7 +389,7 @@ func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) + + if r.isBoundaryDelimiterLine(line) { + r.partsRead++ +- bp, err := newPart(r, rawPart, maxMIMEHeaderSize) ++ bp, err := newPart(r, rawPart, maxMIMEHeaderSize, maxMIMEHeaders) + if err != nil { + return nil, err + } +diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go +index 6836928c9e..25aa6e2092 100644 +--- a/src/mime/multipart/readmimeheader.go ++++ b/src/mime/multipart/readmimeheader.go +@@ -11,4 +11,4 @@ import ( + // readMIMEHeader is defined in package net/textproto. + // + //go:linkname readMIMEHeader net/textproto.readMIMEHeader +-func readMIMEHeader(r *textproto.Reader, lim int64) (textproto.MIMEHeader, error) ++func readMIMEHeader(r *textproto.Reader, maxMemory, maxHeaders int64) (textproto.MIMEHeader, error) +diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go +index c1284fde25..1ad4416ee1 100644 +--- a/src/net/textproto/reader.go ++++ b/src/net/textproto/reader.go +@@ -483,12 +483,12 @@ var colon = []byte(":") + // "Long-Key": {"Even Longer Value"}, + // } + func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { +- return readMIMEHeader(r, math.MaxInt64) ++ return readMIMEHeader(r, math.MaxInt64, math.MaxInt64) + } + + // readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size. + // It is called by the mime/multipart package. +-func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { ++func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) { + // Avoid lots of small slice allocations later by allocating one + // large one ahead of time which we'll cut up into smaller + // slices. If this isn't big enough later, we allocate small ones. +@@ -506,7 +506,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { + // Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry. + // Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large + // MIMEHeaders average about 200 bytes per entry. +- lim -= 400 ++ maxMemory -= 400 + const mapEntryOverhead = 200 + + // The first line cannot start with a leading space. +@@ -538,16 +538,21 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { + continue + } + ++ maxHeaders-- ++ if maxHeaders < 0 { ++ return nil, errors.New("message too large") ++ } ++ + // Skip initial spaces in value. + value := string(bytes.TrimLeft(v, " \t")) + + vv := m[key] + if vv == nil { +- lim -= int64(len(key)) +- lim -= mapEntryOverhead ++ maxMemory -= int64(len(key)) ++ maxMemory -= mapEntryOverhead + } +- lim -= int64(len(value)) +- if lim < 0 { ++ maxMemory -= int64(len(value)) ++ if maxMemory < 0 { + // TODO: This should be a distinguishable error (ErrMessageTooLarge) + // to allow mime/multipart to detect it. + return m, errors.New("message too large") +-- +2.37.1 + diff --git a/0003-release-branch.go1.19-go-scanner-reject-large-line.patch b/0012-release-branch.go1.19-go-scanner-reject-large-line-a.patch similarity index 99% rename from 0003-release-branch.go1.19-go-scanner-reject-large-line.patch rename to 0012-release-branch.go1.19-go-scanner-reject-large-line-a.patch index 86875be..5c90264 100644 --- a/0003-release-branch.go1.19-go-scanner-reject-large-line.patch +++ b/0012-release-branch.go1.19-go-scanner-reject-large-line-a.patch @@ -87,5 +87,5 @@ index 07e07581f7..6a7e30bb2f 100644 return } -- -2.33.0 +2.37.1 diff --git a/0004-release-branch.go1.19-html-template-disallow-actions.patch b/0013-release-branch.go1.19-html-template-disallow-actions.patch similarity index 99% rename from 0004-release-branch.go1.19-html-template-disallow-actions.patch rename to 0013-release-branch.go1.19-html-template-disallow-actions.patch index dc0008a..1fad1ef 100644 --- a/0004-release-branch.go1.19-html-template-disallow-actions.patch +++ b/0013-release-branch.go1.19-html-template-disallow-actions.patch @@ -365,5 +365,5 @@ index 06df679330..92eb351906 100644 specials = `\/[]` } -- -2.33.0 +2.37.1 diff --git a/0014-release-branch.go1.19-html-template-disallow-angle-b.patch b/0014-release-branch.go1.19-html-template-disallow-angle-b.patch new file mode 100644 index 0000000..5190d57 --- /dev/null +++ b/0014-release-branch.go1.19-html-template-disallow-angle-b.patch @@ -0,0 +1,63 @@ +From e49282327b05192e46086bf25fd3ac691205fe80 Mon Sep 17 00:00:00 2001 +From: Roland Shoemaker +Date: Thu, 13 Apr 2023 15:40:44 -0700 +Subject: [PATCH] [release-branch.go1.19] html/template: disallow angle + brackets in CSS values + +Angle brackets should not appear in CSS contexts, as they may affect +token boundaries (such as closing a