diff --git a/0001-Enable-go-plugin-support-for-riscv64.patch b/0001-Enable-go-plugin-support-for-riscv64-based-on-work-b.patch similarity index 55% rename from 0001-Enable-go-plugin-support-for-riscv64.patch rename to 0001-Enable-go-plugin-support-for-riscv64-based-on-work-b.patch index 38912e9ae99feb1240ffca270ebf2b456aab0ef3..46a4c7b24f3c5531dadd06d9af9e368774713541 100644 --- a/0001-Enable-go-plugin-support-for-riscv64.patch +++ b/0001-Enable-go-plugin-support-for-riscv64-based-on-work-b.patch @@ -1,40 +1,40 @@ -From 019ec52ee5e7dec0c3a7937025948a742822a052 Mon Sep 17 00:00:00 2001 -From: misaka00251 -Date: Mon, 27 Mar 2023 10:27:38 +0800 +From 57777dcbc3d05c12ac2b227e7afd5435bf84128c Mon Sep 17 00:00:00 2001 +From: hanchao +Date: Mon, 3 Jul 2023 21:20:32 +0800 Subject: [PATCH] Enable go plugin support for riscv64 (based on work by yangjinghua) --- - src/cmd/internal/sys/supported.go | 2 +- src/cmd/link/internal/ld/config.go | 2 +- + src/internal/platform/supported.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) -diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go -index 1d74f6b..4fa8ed6 100644 ---- a/src/cmd/internal/sys/supported.go -+++ b/src/cmd/internal/sys/supported.go -@@ -142,7 +142,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { - - case "plugin": - switch platform { -- case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le", -+ case "linux/amd64", "linux/arm", "linux/arm64", "linux/riscv64", "linux/386", "linux/s390x", "linux/ppc64le", - "android/amd64", "android/arm", "android/arm64", "android/386", - "darwin/amd64", "darwin/arm64", - "freebsd/amd64": diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go -index 4dd43a1..af5d8c5 100644 +index ba74b6f..836c10d 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go -@@ -95,7 +95,7 @@ func (mode *BuildMode) Set(s string) error { +@@ -84,7 +84,7 @@ func (mode *BuildMode) Set(s string) error { switch buildcfg.GOOS { case "linux": switch buildcfg.GOARCH { -- case "386", "amd64", "arm", "arm64", "s390x", "ppc64le": -+ case "386", "amd64", "arm", "arm64", "riscv64", "s390x", "ppc64le": +- case "386", "amd64", "arm", "arm64", "ppc64le", "s390x": ++ case "386", "amd64", "arm", "arm64", "riscv64", "ppc64le", "s390x": default: return badmode() } +diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go +index 046352f..644822f 100644 +--- a/src/internal/platform/supported.go ++++ b/src/internal/platform/supported.go +@@ -180,7 +180,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { + + case "plugin": + switch platform { +- case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le", ++ case "linux/amd64", "linux/arm", "linux/arm64", "linux/riscv64", "linux/386", "linux/s390x", "linux/ppc64le", + "android/amd64", "android/arm", "android/arm64", "android/386", + "darwin/amd64", "darwin/arm64", + "freebsd/amd64": -- -2.37.1 (Apple Git-137.1) +2.33.0 diff --git a/0002-runtime-support-riscv64-SV57-mode.patch b/0002-runtime-support-riscv64-SV57-mode.patch deleted file mode 100644 index 445761efe7592edb4a006792050d07b233bbfe47..0000000000000000000000000000000000000000 --- a/0002-runtime-support-riscv64-SV57-mode.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 1e3c19f3fee12e5e2b7802a54908a4d4d03960da Mon Sep 17 00:00:00 2001 -From: Dmitry Vyukov -Date: Fri, 27 May 2022 18:55:35 +0200 -Subject: [PATCH] runtime: support riscv64 SV57 mode - -riscv64 has SV57 mode when user-space VA is 56 bits. -Linux kernel recently got support for this mode and Go binaries started crashing as: - -runtime: lfstack.push invalid packing: node=0xffffff5908a940 cnt=0x1 -packed=0xffff5908a9400001 -> node=0xffff5908a940 - -Adjust lfstack code to use only 8 top bits of pointers on riscv64. - -For context see: -https://groups.google.com/g/syzkaller-bugs/c/lU0GQTZoNQQ/m/O_c3vmE3AAAJ - -Update #54104 - -Change-Id: Ib5d3d6a79c0c6eddf11618d73fcc8bc1832a9c25 -Reviewed-on: https://go-review.googlesource.com/c/go/+/409055 -Reviewed-by: Joel Sing -Reviewed-by: Meng Zhuo -Reviewed-by: Michael Knyszek -Reviewed-by: Cherry Mui ---- - -diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go -index 154130c..88cbd3b 100644 ---- a/src/runtime/lfstack_64bit.go -+++ b/src/runtime/lfstack_64bit.go -@@ -36,12 +36,21 @@ - // We use one bit to distinguish between the two ranges. - aixAddrBits = 57 - aixCntBits = 64 - aixAddrBits + 3 -+ -+ // riscv64 SV57 mode gives 56 bits of userspace VA. -+ // lfstack code supports it, but broader support for SV57 mode is incomplete, -+ // and there may be other issues (see #54104). -+ riscv64AddrBits = 56 -+ riscv64CntBits = 64 - riscv64AddrBits + 3 - ) - - func lfstackPack(node *lfnode, cnt uintptr) uint64 { - if GOARCH == "ppc64" && GOOS == "aix" { - return uint64(uintptr(unsafe.Pointer(node)))<<(64-aixAddrBits) | uint64(cnt&(1<> aixCntBits << 3) | 0xa<<56))) - } -+ if GOARCH == "riscv64" { -+ return (*lfnode)(unsafe.Pointer(uintptr(val >> riscv64CntBits << 3))) -+ } - return (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3))) - } 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 deleted file mode 100644 index f0b7fe15625b74047796dbd5536ecc0b1f8a0004..0000000000000000000000000000000000000000 --- a/0003-release-branch.go1.19-path-filepath-do-not-Clean-a-..patch +++ /dev/null @@ -1,134 +0,0 @@ -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 deleted file mode 100644 index 70803b7513e24fec7770a803d5c8c957a231238a..0000000000000000000000000000000000000000 --- a/0004-release-branch.go1.19-mime-multipart-limit-memory-in.patch +++ /dev/null @@ -1,644 +0,0 @@ -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 deleted file mode 100644 index 03d46ec373b615ed8a9c2a55dde8fa0d975076ad..0000000000000000000000000000000000000000 --- a/0005-release-branch.go1.19-crypto-tls-replace-all-usages-.patch +++ /dev/null @@ -1,2408 +0,0 @@ -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 deleted file mode 100644 index 0f588f6009ccc44a89d4b0e825c0d7d9d9dc3b01..0000000000000000000000000000000000000000 --- a/0006-release-branch.go1.19-net-http-update-bundled-golang.patch +++ /dev/null @@ -1,168 +0,0 @@ -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 deleted file mode 100644 index cbdb22061e7189fa877a095bc984397bfa55440d..0000000000000000000000000000000000000000 --- a/0007-release-branch.go1.19-crypto-internal-nistec-reduce-.patch +++ /dev/null @@ -1,206 +0,0 @@ -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/0008-release-branch.go1.19-net-textproto-avoid-overpredic.patch b/0008-release-branch.go1.19-net-textproto-avoid-overpredic.patch deleted file mode 100644 index 1469715cb608ca26c8ccb3ebf86527cf3a240ff5..0000000000000000000000000000000000000000 --- a/0008-release-branch.go1.19-net-textproto-avoid-overpredic.patch +++ /dev/null @@ -1,188 +0,0 @@ -From d6759e7a059f4208f07aa781402841d7ddaaef96 Mon Sep 17 00:00:00 2001 -From: Damien Neil -Date: Fri, 10 Mar 2023 14:21:05 -0800 -Subject: [PATCH] [release-branch.go1.19] net/textproto: avoid overpredicting - the number of MIME header keys - -A parsed MIME header is a map[string][]string. In the common case, -a header contains many one-element []string slices. To avoid -allocating a separate slice for each key, ReadMIMEHeader looks -ahead in the input to predict the number of keys that will be -parsed, and allocates a single []string of that length. -The individual slices are then allocated out of the larger one. - -The prediction of the number of header keys was done by counting -newlines in the input buffer, which does not take into account -header continuation lines (where a header key/value spans multiple -lines) or the end of the header block and the start of the body. -This could lead to a substantial amount of overallocation, for -example when the body consists of nothing but a large block of -newlines. - -Fix header key count prediction to take into account the end of -the headers (indicated by a blank line) and continuation lines -(starting with whitespace). - -Thanks to Jakob Ackermann (@das7pad) for reporting this issue. - -Fixes CVE-2023-24534 -For #58975 -Fixes #59267 - -Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802452 -Run-TryBot: Damien Neil -Reviewed-by: Roland Shoemaker -Reviewed-by: Julie Qiu -(cherry picked from commit f739f080a72fd5b06d35c8e244165159645e2ed6) -Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802393 -Reviewed-by: Damien Neil -Run-TryBot: Roland Shoemaker -Change-Id: I675451438d619a9130360c56daf529559004903f -Reviewed-on: https://go-review.googlesource.com/c/go/+/481982 -Run-TryBot: Michael Knyszek -TryBot-Result: Gopher Robot -Reviewed-by: Matthew Dempsky -Auto-Submit: Michael Knyszek ---- - src/net/textproto/reader.go | 24 ++++++++++--- - src/net/textproto/reader_test.go | 59 ++++++++++++++++++++++++++++++++ - 2 files changed, 79 insertions(+), 4 deletions(-) - -diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go -index b37be54d67..9a21777df8 100644 ---- a/src/net/textproto/reader.go -+++ b/src/net/textproto/reader.go -@@ -493,8 +493,11 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { - // large one ahead of time which we'll cut up into smaller - // slices. If this isn't big enough later, we allocate small ones. - var strs []string -- hint := r.upcomingHeaderNewlines() -+ hint := r.upcomingHeaderKeys() - if hint > 0 { -+ if hint > 1000 { -+ hint = 1000 // set a cap to avoid overallocation -+ } - strs = make([]string, hint) - } - -@@ -577,9 +580,9 @@ func mustHaveFieldNameColon(line []byte) error { - - var nl = []byte("\n") - --// upcomingHeaderNewlines returns an approximation of the number of newlines -+// upcomingHeaderKeys returns an approximation of the number of keys - // that will be in this header. If it gets confused, it returns 0. --func (r *Reader) upcomingHeaderNewlines() (n int) { -+func (r *Reader) upcomingHeaderKeys() (n int) { - // Try to determine the 'hint' size. - r.R.Peek(1) // force a buffer load if empty - s := r.R.Buffered() -@@ -587,7 +590,20 @@ func (r *Reader) upcomingHeaderNewlines() (n int) { - return - } - peek, _ := r.R.Peek(s) -- return bytes.Count(peek, nl) -+ for len(peek) > 0 && n < 1000 { -+ var line []byte -+ line, peek, _ = bytes.Cut(peek, nl) -+ if len(line) == 0 || (len(line) == 1 && line[0] == '\r') { -+ // Blank line separating headers from the body. -+ break -+ } -+ if line[0] == ' ' || line[0] == '\t' { -+ // Folded continuation of the previous line. -+ continue -+ } -+ n++ -+ } -+ return n - } - - // CanonicalMIMEHeaderKey returns the canonical format of the -diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go -index d11d40f1cf..f3c372ce03 100644 ---- a/src/net/textproto/reader_test.go -+++ b/src/net/textproto/reader_test.go -@@ -10,6 +10,7 @@ import ( - "io" - "net" - "reflect" -+ "runtime" - "strings" - "sync" - "testing" -@@ -129,6 +130,42 @@ func TestReadMIMEHeaderSingle(t *testing.T) { - } - } - -+// TestReaderUpcomingHeaderKeys is testing an internal function, but it's very -+// difficult to test well via the external API. -+func TestReaderUpcomingHeaderKeys(t *testing.T) { -+ for _, test := range []struct { -+ input string -+ want int -+ }{{ -+ input: "", -+ want: 0, -+ }, { -+ input: "A: v", -+ want: 1, -+ }, { -+ input: "A: v\r\nB: v\r\n", -+ want: 2, -+ }, { -+ input: "A: v\nB: v\n", -+ want: 2, -+ }, { -+ input: "A: v\r\n continued\r\n still continued\r\nB: v\r\n\r\n", -+ want: 2, -+ }, { -+ input: "A: v\r\n\r\nB: v\r\nC: v\r\n", -+ want: 1, -+ }, { -+ input: "A: v" + strings.Repeat("\n", 1000), -+ want: 1, -+ }} { -+ r := reader(test.input) -+ got := r.upcomingHeaderKeys() -+ if test.want != got { -+ t.Fatalf("upcomingHeaderKeys(%q): %v; want %v", test.input, got, test.want) -+ } -+ } -+} -+ - func TestReadMIMEHeaderNoKey(t *testing.T) { - r := reader(": bar\ntest-1: 1\n\n") - m, err := r.ReadMIMEHeader() -@@ -225,6 +262,28 @@ func TestReadMIMEHeaderTrimContinued(t *testing.T) { - } - } - -+// Test that reading a header doesn't overallocate. Issue 58975. -+func TestReadMIMEHeaderAllocations(t *testing.T) { -+ var totalAlloc uint64 -+ const count = 200 -+ for i := 0; i < count; i++ { -+ r := reader("A: b\r\n\r\n" + strings.Repeat("\n", 4096)) -+ var m1, m2 runtime.MemStats -+ runtime.ReadMemStats(&m1) -+ _, err := r.ReadMIMEHeader() -+ if err != nil { -+ t.Fatalf("ReadMIMEHeader: %v", err) -+ } -+ runtime.ReadMemStats(&m2) -+ totalAlloc += m2.TotalAlloc - m1.TotalAlloc -+ } -+ // 32k is large and we actually allocate substantially less, -+ // but prior to the fix for #58975 we allocated ~400k in this case. -+ if got, want := totalAlloc/count, uint64(32768); got > want { -+ t.Fatalf("ReadMIMEHeader allocated %v bytes, want < %v", got, want) -+ } -+} -+ - type readResponseTest struct { - in string - inCode int --- -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 deleted file mode 100644 index 715578d13124ef5195ad371fd61e6fe36fa3603f..0000000000000000000000000000000000000000 --- a/0009-release-branch.go1.19-mime-multipart-avoid-excessive.patch +++ /dev/null @@ -1,133 +0,0 @@ -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 deleted file mode 100644 index e48292fa31e443ead533c96f8952fe08de55fcba..0000000000000000000000000000000000000000 --- a/0010-release-branch.go1.19-net-textproto-mime-multipart-i.patch +++ /dev/null @@ -1,183 +0,0 @@ -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 deleted file mode 100644 index a52e793d5a6b110449291427d011e88bb9246b31..0000000000000000000000000000000000000000 --- a/0011-release-branch.go1.19-mime-multipart-limit-parsed-mi.patch +++ /dev/null @@ -1,344 +0,0 @@ -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/0012-release-branch.go1.19-go-scanner-reject-large-line-a.patch b/0012-release-branch.go1.19-go-scanner-reject-large-line-a.patch deleted file mode 100644 index 5c90264567bbc2facc33f44016b040c0f099df13..0000000000000000000000000000000000000000 --- a/0012-release-branch.go1.19-go-scanner-reject-large-line-a.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 126a1d02da82f93ede7ce0bd8d3c51ef627f2104 Mon Sep 17 00:00:00 2001 -From: Damien Neil -Date: Wed, 22 Mar 2023 09:33:22 -0700 -Subject: [PATCH] [release-branch.go1.19] go/scanner: reject large line and - column numbers in //line directives - -Setting a large line or column number using a //line directive can cause -integer overflow even in small source files. - -Limit line and column numbers in //line directives to 2^30-1, which -is small enough to avoid int32 overflow on all reasonbly-sized files. - -Fixes CVE-2023-24537 -Fixes #59273 -For #59180 - -Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802456 -Reviewed-by: Julie Qiu -Reviewed-by: Roland Shoemaker -Run-TryBot: Damien Neil -Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802611 -Reviewed-by: Damien Neil -Change-Id: Ifdfa192d54f722d781a4d8c5f35b5fb72d122168 -Reviewed-on: https://go-review.googlesource.com/c/go/+/481986 -Reviewed-by: Matthew Dempsky -TryBot-Result: Gopher Robot -Run-TryBot: Michael Knyszek -Auto-Submit: Michael Knyszek ---- - src/go/parser/parser_test.go | 16 ++++++++++++++++ - src/go/scanner/scanner.go | 7 +++++-- - 2 files changed, 21 insertions(+), 2 deletions(-) - -diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go -index 0c278924c9..15b265ffc3 100644 ---- a/src/go/parser/parser_test.go -+++ b/src/go/parser/parser_test.go -@@ -742,3 +742,19 @@ func TestScopeDepthLimit(t *testing.T) { - } - } - } -+ -+// TestIssue59180 tests that line number overflow doesn't cause an infinite loop. -+func TestIssue59180(t *testing.T) { -+ testcases := []string{ -+ "package p\n//line :9223372036854775806\n\n//", -+ "package p\n//line :1:9223372036854775806\n\n//", -+ "package p\n//line file:9223372036854775806\n\n//", -+ } -+ -+ for _, src := range testcases { -+ _, err := ParseFile(token.NewFileSet(), "", src, ParseComments) -+ if err == nil { -+ t.Errorf("ParseFile(%s) succeeded unexpectedly", src) -+ } -+ } -+} -diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go -index 07e07581f7..6a7e30bb2f 100644 ---- a/src/go/scanner/scanner.go -+++ b/src/go/scanner/scanner.go -@@ -246,13 +246,16 @@ func (s *Scanner) updateLineInfo(next, offs int, text []byte) { - return - } - -+ // Put a cap on the maximum size of line and column numbers. -+ // 30 bits allows for some additional space before wrapping an int32. -+ const maxLineCol = 1<<30 - 1 - var line, col int - i2, n2, ok2 := trailingDigits(text[:i-1]) - if ok2 { - //line filename:line:col - i, i2 = i2, i - line, col = n2, n -- if col == 0 { -+ if col == 0 || col > maxLineCol { - s.error(offs+i2, "invalid column number: "+string(text[i2:])) - return - } -@@ -262,7 +265,7 @@ func (s *Scanner) updateLineInfo(next, offs int, text []byte) { - line = n - } - -- if line == 0 { -+ if line == 0 || line > maxLineCol { - s.error(offs+i, "invalid line number: "+string(text[i:])) - return - } --- -2.37.1 - diff --git a/0013-release-branch.go1.19-html-template-disallow-actions.patch b/0013-release-branch.go1.19-html-template-disallow-actions.patch deleted file mode 100644 index 1fad1ef1bdf19fb54de9db6f8ce5e7976eb340da..0000000000000000000000000000000000000000 --- a/0013-release-branch.go1.19-html-template-disallow-actions.patch +++ /dev/null @@ -1,369 +0,0 @@ -From b1e3ecfa06b67014429a197ec5e134ce4303ad9b Mon Sep 17 00:00:00 2001 -From: Roland Shoemaker -Date: Mon, 20 Mar 2023 11:01:13 -0700 -Subject: [PATCH] [release-branch.go1.19] html/template: disallow actions in JS - template literals - -ECMAScript 6 introduced template literals[0][1] which are delimited with -backticks. These need to be escaped in a similar fashion to the -delimiters for other string literals. Additionally template literals can -contain special syntax for string interpolation. - -There is no clear way to allow safe insertion of actions within JS -template literals, as handling (JS) string interpolation inside of these -literals is rather complex. As such we've chosen to simply disallow -template actions within these template literals. - -A new error code is added for this parsing failure case, errJsTmplLit, -but it is unexported as it is not backwards compatible with other minor -release versions to introduce an API change in a minor release. We will -export this code in the next major release. - -The previous behavior (with the cavet that backticks are now escaped -properly) can be re-enabled with GODEBUG=jstmpllitinterp=1. - -This change subsumes CL471455. - -Thanks to Sohom Datta, Manipal Institute of Technology, for reporting -this issue. - -Fixes CVE-2023-24538 -For #59234 -Fixes #59271 - -[0] https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-template-literals -[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals - -Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802457 -Reviewed-by: Damien Neil -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/+/1802612 -Run-TryBot: Roland Shoemaker -Change-Id: Ic7f10595615f2b2740d9c85ad7ef40dc0e78c04c -Reviewed-on: https://go-review.googlesource.com/c/go/+/481987 -Auto-Submit: Michael Knyszek -TryBot-Result: Gopher Robot -Run-TryBot: Michael Knyszek -Reviewed-by: Matthew Dempsky ---- - src/html/template/context.go | 2 + - src/html/template/error.go | 13 ++++++ - src/html/template/escape.go | 11 ++++++ - src/html/template/escape_test.go | 66 +++++++++++++++++-------------- - src/html/template/js.go | 2 + - src/html/template/js_test.go | 2 +- - src/html/template/jsctx_string.go | 9 +++++ - src/html/template/state_string.go | 37 ++++++++++++++++- - src/html/template/transition.go | 7 +++- - 9 files changed, 116 insertions(+), 33 deletions(-) - -diff --git a/src/html/template/context.go b/src/html/template/context.go -index a97c8be56f..c28fb0c5ea 100644 ---- a/src/html/template/context.go -+++ b/src/html/template/context.go -@@ -120,6 +120,8 @@ const ( - stateJSDqStr - // stateJSSqStr occurs inside a JavaScript single quoted string. - stateJSSqStr -+ // stateJSBqStr occurs inside a JavaScript back quoted string. -+ stateJSBqStr - // stateJSRegexp occurs inside a JavaScript regexp literal. - stateJSRegexp - // stateJSBlockCmt occurs inside a JavaScript /* block comment */. -diff --git a/src/html/template/error.go b/src/html/template/error.go -index 5c51f772cb..d7d6f5b3ab 100644 ---- a/src/html/template/error.go -+++ b/src/html/template/error.go -@@ -214,6 +214,19 @@ const ( - // pipeline occurs in an unquoted attribute value context, "html" is - // disallowed. Avoid using "html" and "urlquery" entirely in new templates. - ErrPredefinedEscaper -+ -+ // errJSTmplLit: "... appears in a JS template literal" -+ // Example: -+ // -+ // Discussion: -+ // Package html/template does not support actions inside of JS template -+ // literals. -+ // -+ // TODO(rolandshoemaker): we cannot add this as an exported error in a minor -+ // release, since it is backwards incompatible with the other minor -+ // releases. As such we need to leave it unexported, and then we'll add it -+ // in the next major release. -+ errJSTmplLit - ) - - func (e *Error) Error() string { -diff --git a/src/html/template/escape.go b/src/html/template/escape.go -index 54fbcdca33..3d4cc19b5d 100644 ---- a/src/html/template/escape.go -+++ b/src/html/template/escape.go -@@ -8,6 +8,7 @@ import ( - "bytes" - "fmt" - "html" -+ "internal/godebug" - "io" - "text/template" - "text/template/parse" -@@ -223,6 +224,16 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { - c.jsCtx = jsCtxDivOp - case stateJSDqStr, stateJSSqStr: - s = append(s, "_html_template_jsstrescaper") -+ case stateJSBqStr: -+ debugAllowActionJSTmpl := godebug.Get("jstmpllitinterp") -+ if debugAllowActionJSTmpl == "1" { -+ s = append(s, "_html_template_jsstrescaper") -+ } else { -+ return context{ -+ state: stateError, -+ err: errorf(errJSTmplLit, n, n.Line, "%s appears in a JS template literal", n), -+ } -+ } - case stateJSRegexp: - s = append(s, "_html_template_jsregexpescaper") - case stateCSS: -diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go -index 58f3f271b7..972b00b921 100644 ---- a/src/html/template/escape_test.go -+++ b/src/html/template/escape_test.go -@@ -681,35 +681,31 @@ func TestEscape(t *testing.T) { - } - - for _, test := range tests { -- tmpl := New(test.name) -- tmpl = Must(tmpl.Parse(test.input)) -- // Check for bug 6459: Tree field was not set in Parse. -- if tmpl.Tree != tmpl.text.Tree { -- t.Errorf("%s: tree not set properly", test.name) -- continue -- } -- b := new(bytes.Buffer) -- if err := tmpl.Execute(b, data); err != nil { -- t.Errorf("%s: template execution failed: %s", test.name, err) -- continue -- } -- if w, g := test.output, b.String(); w != g { -- t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g) -- continue -- } -- b.Reset() -- if err := tmpl.Execute(b, pdata); err != nil { -- t.Errorf("%s: template execution failed for pointer: %s", test.name, err) -- continue -- } -- if w, g := test.output, b.String(); w != g { -- t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g) -- continue -- } -- if tmpl.Tree != tmpl.text.Tree { -- t.Errorf("%s: tree mismatch", test.name) -- continue -- } -+ t.Run(test.name, func(t *testing.T) { -+ tmpl := New(test.name) -+ tmpl = Must(tmpl.Parse(test.input)) -+ // Check for bug 6459: Tree field was not set in Parse. -+ if tmpl.Tree != tmpl.text.Tree { -+ t.Fatalf("%s: tree not set properly", test.name) -+ } -+ b := new(strings.Builder) -+ if err := tmpl.Execute(b, data); err != nil { -+ t.Fatalf("%s: template execution failed: %s", test.name, err) -+ } -+ if w, g := test.output, b.String(); w != g { -+ t.Fatalf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g) -+ } -+ b.Reset() -+ if err := tmpl.Execute(b, pdata); err != nil { -+ t.Fatalf("%s: template execution failed for pointer: %s", test.name, err) -+ } -+ if w, g := test.output, b.String(); w != g { -+ t.Fatalf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g) -+ } -+ if tmpl.Tree != tmpl.text.Tree { -+ t.Fatalf("%s: tree mismatch", test.name) -+ } -+ }) - } - } - -@@ -936,6 +932,10 @@ func TestErrors(t *testing.T) { - "{{range .Items}}{{if .X}}{{break}}{{end}}{{end}}", - "", - }, -+ { -+ "`", -+ "", -+ }, - // Error cases. - { - "{{if .Cond}}var tmpl = `asd {{.}}`;", -+ `{{.}} appears in a JS template literal`, -+ }, - } - for _, test := range tests { - buf := new(bytes.Buffer) -@@ -1303,6 +1307,10 @@ func TestEscapeText(t *testing.T) { - `= state(len(_state_index)-1) { -diff --git a/src/html/template/transition.go b/src/html/template/transition.go -index 06df679330..92eb351906 100644 ---- a/src/html/template/transition.go -+++ b/src/html/template/transition.go -@@ -27,6 +27,7 @@ var transitionFunc = [...]func(context, []byte) (context, int){ - stateJS: tJS, - stateJSDqStr: tJSDelimited, - stateJSSqStr: tJSDelimited, -+ stateJSBqStr: tJSDelimited, - stateJSRegexp: tJSDelimited, - stateJSBlockCmt: tBlockCmt, - stateJSLineCmt: tLineCmt, -@@ -262,7 +263,7 @@ func tURL(c context, s []byte) (context, int) { - - // tJS is the context transition function for the JS state. - func tJS(c context, s []byte) (context, int) { -- i := bytes.IndexAny(s, `"'/`) -+ i := bytes.IndexAny(s, "\"`'/") - if i == -1 { - // Entire input is non string, comment, regexp tokens. - c.jsCtx = nextJSCtx(s, c.jsCtx) -@@ -274,6 +275,8 @@ func tJS(c context, s []byte) (context, int) { - c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp - case '\'': - c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp -+ case '`': -+ c.state, c.jsCtx = stateJSBqStr, jsCtxRegexp - case '/': - switch { - case i+1 < len(s) && s[i+1] == '/': -@@ -303,6 +306,8 @@ func tJSDelimited(c context, s []byte) (context, int) { - switch c.state { - case stateJSSqStr: - specials = `\'` -+ case stateJSBqStr: -+ specials = "`\\" - case stateJSRegexp: - specials = `\/[]` - } --- -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 deleted file mode 100644 index 5190d57332079dcfbdfcb602ee495c35d567a6cc..0000000000000000000000000000000000000000 --- a/0014-release-branch.go1.19-html-template-disallow-angle-b.patch +++ /dev/null @@ -1,63 +0,0 @@ -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