From 18bab2267ddb865bbff5f778bf60a115b145bd74 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Fri, 2 Sep 2022 20:45:18 -0700 Subject: [PATCH] [go-1.15.15-eks] archive/tar: limit size of headers # AWS EKS Backported To: go-1.15.15-eks Backported On: Wed, 5 Oct 2022 Backported By: rcrozean@amazon.com Backported From: release-branch.go1.15 Source Commit: https://github.com/golang/go/commit/0a723816cd205576945fa57fbdde7e6532d59d08 EKS Patch Source Commit: https://github.com/aws/eks-distro-build-tooling/commit/1de4158fdca55859eb7cc47dccc9aacbfc0697ed For building an internal version of go1.15, we removed the binary information from this patch, describing ./src/archive/tar/pax-bad-hdr-large.tar.bz2 The file itself was added directly to the repository. This was done due to fact that `diff`, used by RPMs for patch application, does not support binary diffs. https://github.com/aws/eks-distro-build-tooling/projects/golang/go/1.15/rpmbuild/SOURCE/pax-bad-hdr-large.tar.bz2 # Originial Information Set a 1MiB limit on special file blocks (PAX headers, GNU long names, GNU link names), to avoid reading arbitrarily large amounts of data into memory. Thanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting this issue. Fixes CVE-2022-2879 Updates #54853 Fixes #55925 Change-Id: I85136d6ff1e0af101a112190e027987ab4335680 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1565555 Reviewed-by: Tatiana Bradley Run-TryBot: Roland Shoemaker Reviewed-by: Roland Shoemaker (cherry picked from commit 6ee768cef6b82adf7a90dcf367a1699ef694f3b2) Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1590622 Reviewed-by: Damien Neil Reviewed-by: Julie Qiu Reviewed-on: https://go-review.googlesource.com/c/go/+/438500 Reviewed-by: Dmitri Shuralyov Reviewed-by: Carlos Amedee Reviewed-by: Dmitri Shuralyov Run-TryBot: Carlos Amedee TryBot-Result: Gopher Robot --- src/archive/tar/format.go | 4 +++ src/archive/tar/reader.go | 14 +++++++-- src/archive/tar/reader_test.go | 11 ++++++- .../tar/testdata/pax-bad-hdr-large.tar.bz2 | Bin 0 -> 156 bytes src/archive/tar/writer.go | 3 ++ src/archive/tar/writer_test.go | 27 ++++++++++++++++++ 6 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/archive/tar/testdata/pax-bad-hdr-large.tar.bz2 diff --git a/src/archive/tar/format.go b/src/archive/tar/format.go index cfe24a5e1d..6642364de1 100644 --- a/src/archive/tar/format.go +++ b/src/archive/tar/format.go @@ -143,6 +143,10 @@ const ( blockSize = 512 // Size of each block in a tar stream nameSize = 100 // Max length of the name field in USTAR format prefixSize = 155 // Max length of the prefix field in USTAR format + + // Max length of a special file (PAX header, GNU long name or link). + // This matches the limit used by libarchive. + maxSpecialFileSize = 1 << 20 ) // blockPadding computes the number of bytes needed to pad offset up to the diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go index 4f9135b791..a776e0be28 100644 --- a/src/archive/tar/reader.go +++ b/src/archive/tar/reader.go @@ -104,7 +104,7 @@ func (tr *Reader) next() (*Header, error) { continue // This is a meta header affecting the next header case TypeGNULongName, TypeGNULongLink: format.mayOnlyBe(FormatGNU) - realname, err := ioutil.ReadAll(tr) + realname, err := readSpecialFile(tr) if err != nil { return nil, err } @@ -294,7 +294,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) { // parsePAX parses PAX headers. // If an extended header (type 'x') is invalid, ErrHeader is returned func parsePAX(r io.Reader) (map[string]string, error) { - buf, err := ioutil.ReadAll(r) + buf, err := readSpecialFile(r) if err != nil { return nil, err } @@ -827,6 +827,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) { return n, err } +// readSpecialFile is like io.ReadAll except it returns +// ErrFieldTooLong if more than maxSpecialFileSize is read. +func readSpecialFile(r io.Reader) ([]byte, error) { + buf, err := ioutil.ReadAll(io.LimitReader(r, maxSpecialFileSize+1)) + if len(buf) > maxSpecialFileSize { + return nil, ErrFieldTooLong + } + return buf, err +} + // discard skips n bytes in r, reporting an error if unable to do so. func discard(r io.Reader, n int64) error { // If possible, Seek to the last byte before the end of the data section. diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go index f153b668de..c68a859ad8 100644 --- a/src/archive/tar/reader_test.go +++ b/src/archive/tar/reader_test.go @@ -6,6 +6,7 @@ package tar import ( "bytes" + "compress/bzip2" "crypto/md5" "errors" "fmt" @@ -244,6 +245,9 @@ func TestReader(t *testing.T) { }, { file: "testdata/pax-bad-hdr-file.tar", err: ErrHeader, + }, { + file: "testdata/pax-bad-hdr-large.tar.bz2", + err: ErrFieldTooLong, }, { file: "testdata/pax-bad-mtime-file.tar", err: ErrHeader, @@ -626,9 +630,14 @@ func TestReader(t *testing.T) { } defer f.Close() + var fr io.Reader = f + if strings.HasSuffix(v.file, ".bz2") { + fr = bzip2.NewReader(fr) + } + // Capture all headers and checksums. var ( - tr = NewReader(f) + tr = NewReader(fr) hdrs []*Header chksums []string rdbuf = make([]byte, 8) diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go index e80498d03e..893eac00ae 100644 --- a/src/archive/tar/writer.go +++ b/src/archive/tar/writer.go @@ -199,6 +199,9 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { flag = TypeXHeader } data := buf.String() + if len(data) > maxSpecialFileSize { + return ErrFieldTooLong + } if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal { return err // Global headers return here } diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go index 30556d27d0..cbf6a85525 100644 --- a/src/archive/tar/writer_test.go +++ b/src/archive/tar/writer_test.go @@ -1007,6 +1007,33 @@ func TestIssue12594(t *testing.T) { } } +func TestWriteLongHeader(t *testing.T) { + for _, test := range []struct { + name string + h *Header + }{{ + name: "name too long", + h: &Header{Name: strings.Repeat("a", maxSpecialFileSize)}, + }, { + name: "linkname too long", + h: &Header{Linkname: strings.Repeat("a", maxSpecialFileSize)}, + }, { + name: "uname too long", + h: &Header{Uname: strings.Repeat("a", maxSpecialFileSize)}, + }, { + name: "gname too long", + h: &Header{Gname: strings.Repeat("a", maxSpecialFileSize)}, + }, { + name: "PAX header too long", + h: &Header{PAXRecords: map[string]string{"GOLANG.x": strings.Repeat("a", maxSpecialFileSize)}}, + }} { + w := NewWriter(ioutil.Discard) + if err := w.WriteHeader(test.h); err != ErrFieldTooLong { + t.Errorf("%v: w.WriteHeader() = %v, want ErrFieldTooLong", test.name, err) + } + } +} + // testNonEmptyWriter wraps an io.Writer and ensures that // Write is never called with an empty buffer. type testNonEmptyWriter struct{ io.Writer } -- 2.37.3