From 6d0f92022ce068a7f371fce7875d9fe8343c09d2 Mon Sep 17 00:00:00 2001 From: ChendongSun Date: Tue, 25 Apr 2023 08:34:49 +0800 Subject: [PATCH] fix CVE-2023-24534 --- ...o1.19-net-textproto-avoid-overpredic.patch | 188 ++++++++++++++++++ golang.spec | 7 +- 2 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch diff --git a/0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch b/0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch new file mode 100644 index 0000000..d963083 --- /dev/null +++ b/0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch @@ -0,0 +1,188 @@ +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.33.0 + diff --git a/golang.spec b/golang.spec index 372c157..908afb2 100644 --- a/golang.spec +++ b/golang.spec @@ -62,7 +62,7 @@ Name: golang Version: 1.19.4 -Release: 4 +Release: 5 Summary: The Go Programming Language License: BSD and Public Domain URL: https://golang.org/ @@ -71,7 +71,7 @@ Patch0: 0001-Enable-go-plugin-support-for-riscv64.patch Patch1: 0002-runtime-support-riscv64-SV57-mode.patch Patch2: 0003-release-branch.go1.19-go-scanner-reject-large-line.patch Patch3: 0004-release-branch.go1.19-html-template-disallow-actions.patch - +Patch4: 0005-release-branch.go1.19-net-textproto-avoid-overpredic.patch %if !%{golang_bootstrap} BuildRequires: gcc-go >= 5 %else @@ -391,6 +391,9 @@ fi %files devel -f go-tests.list -f go-misc.list -f go-src.list %changelog +* Sat May 6 2023 sunchendong - 1.19.4-5 +- fix CVE-2023-24534 + * Fri Apr 21 2023 sunchendong - 1.19.4-4 - fix CVE-2023-24538 -- Gitee