1*4882a593SmuzhiyunFrom d064ed520a7cc6b480f9565e30751e695d394f4e Mon Sep 17 00:00:00 2001 2*4882a593SmuzhiyunFrom: Damien Neil <dneil@google.com> 3*4882a593SmuzhiyunDate: Fri, 2 Sep 2022 20:45:18 -0700 4*4882a593SmuzhiyunSubject: [PATCH] archive/tar: limit size of headers 5*4882a593Smuzhiyun 6*4882a593SmuzhiyunSet a 1MiB limit on special file blocks (PAX headers, GNU long names, 7*4882a593SmuzhiyunGNU link names), to avoid reading arbitrarily large amounts of data 8*4882a593Smuzhiyuninto memory. 9*4882a593Smuzhiyun 10*4882a593SmuzhiyunThanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting 11*4882a593Smuzhiyunthis issue. 12*4882a593Smuzhiyun 13*4882a593SmuzhiyunFixes CVE-2022-2879 14*4882a593SmuzhiyunUpdates #54853 15*4882a593SmuzhiyunFixes #55925 16*4882a593Smuzhiyun 17*4882a593SmuzhiyunChange-Id: I85136d6ff1e0af101a112190e027987ab4335680 18*4882a593SmuzhiyunReviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1565555 19*4882a593SmuzhiyunReviewed-by: Tatiana Bradley <tatianabradley@google.com> 20*4882a593SmuzhiyunRun-TryBot: Roland Shoemaker <bracewell@google.com> 21*4882a593SmuzhiyunReviewed-by: Roland Shoemaker <bracewell@google.com> 22*4882a593Smuzhiyun(cherry picked from commit 6ee768cef6b82adf7a90dcf367a1699ef694f3b2) 23*4882a593SmuzhiyunReviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1590622 24*4882a593SmuzhiyunReviewed-by: Damien Neil <dneil@google.com> 25*4882a593SmuzhiyunReviewed-by: Julie Qiu <julieqiu@google.com> 26*4882a593SmuzhiyunReviewed-on: https://go-review.googlesource.com/c/go/+/438500 27*4882a593SmuzhiyunReviewed-by: Dmitri Shuralyov <dmitshur@golang.org> 28*4882a593SmuzhiyunReviewed-by: Carlos Amedee <carlos@golang.org> 29*4882a593SmuzhiyunReviewed-by: Dmitri Shuralyov <dmitshur@google.com> 30*4882a593SmuzhiyunRun-TryBot: Carlos Amedee <carlos@golang.org> 31*4882a593SmuzhiyunTryBot-Result: Gopher Robot <gobot@golang.org> 32*4882a593Smuzhiyun 33*4882a593SmuzhiyunCVE: CVE-2022-2879 34*4882a593SmuzhiyunUpstream-Status: Backport [0a723816cd205576945fa57fbdde7e6532d59d08] 35*4882a593SmuzhiyunSigned-off-by: Sakib Sajal <sakib.sajal@windriver.com> 36*4882a593Smuzhiyun--- 37*4882a593Smuzhiyun src/archive/tar/format.go | 4 ++++ 38*4882a593Smuzhiyun src/archive/tar/reader.go | 14 ++++++++++++-- 39*4882a593Smuzhiyun src/archive/tar/reader_test.go | 8 +++++++- 40*4882a593Smuzhiyun src/archive/tar/writer.go | 3 +++ 41*4882a593Smuzhiyun src/archive/tar/writer_test.go | 27 +++++++++++++++++++++++++++ 42*4882a593Smuzhiyun 5 files changed, 53 insertions(+), 3 deletions(-) 43*4882a593Smuzhiyun 44*4882a593Smuzhiyundiff --git a/src/archive/tar/format.go b/src/archive/tar/format.go 45*4882a593Smuzhiyunindex cfe24a5..6642364 100644 46*4882a593Smuzhiyun--- a/src/archive/tar/format.go 47*4882a593Smuzhiyun+++ b/src/archive/tar/format.go 48*4882a593Smuzhiyun@@ -143,6 +143,10 @@ const ( 49*4882a593Smuzhiyun blockSize = 512 // Size of each block in a tar stream 50*4882a593Smuzhiyun nameSize = 100 // Max length of the name field in USTAR format 51*4882a593Smuzhiyun prefixSize = 155 // Max length of the prefix field in USTAR format 52*4882a593Smuzhiyun+ 53*4882a593Smuzhiyun+ // Max length of a special file (PAX header, GNU long name or link). 54*4882a593Smuzhiyun+ // This matches the limit used by libarchive. 55*4882a593Smuzhiyun+ maxSpecialFileSize = 1 << 20 56*4882a593Smuzhiyun ) 57*4882a593Smuzhiyun 58*4882a593Smuzhiyun // blockPadding computes the number of bytes needed to pad offset up to the 59*4882a593Smuzhiyundiff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go 60*4882a593Smuzhiyunindex 1b1d5b4..f645af8 100644 61*4882a593Smuzhiyun--- a/src/archive/tar/reader.go 62*4882a593Smuzhiyun+++ b/src/archive/tar/reader.go 63*4882a593Smuzhiyun@@ -103,7 +103,7 @@ func (tr *Reader) next() (*Header, error) { 64*4882a593Smuzhiyun continue // This is a meta header affecting the next header 65*4882a593Smuzhiyun case TypeGNULongName, TypeGNULongLink: 66*4882a593Smuzhiyun format.mayOnlyBe(FormatGNU) 67*4882a593Smuzhiyun- realname, err := io.ReadAll(tr) 68*4882a593Smuzhiyun+ realname, err := readSpecialFile(tr) 69*4882a593Smuzhiyun if err != nil { 70*4882a593Smuzhiyun return nil, err 71*4882a593Smuzhiyun } 72*4882a593Smuzhiyun@@ -293,7 +293,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) { 73*4882a593Smuzhiyun // parsePAX parses PAX headers. 74*4882a593Smuzhiyun // If an extended header (type 'x') is invalid, ErrHeader is returned 75*4882a593Smuzhiyun func parsePAX(r io.Reader) (map[string]string, error) { 76*4882a593Smuzhiyun- buf, err := io.ReadAll(r) 77*4882a593Smuzhiyun+ buf, err := readSpecialFile(r) 78*4882a593Smuzhiyun if err != nil { 79*4882a593Smuzhiyun return nil, err 80*4882a593Smuzhiyun } 81*4882a593Smuzhiyun@@ -826,6 +826,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) { 82*4882a593Smuzhiyun return n, err 83*4882a593Smuzhiyun } 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun+// readSpecialFile is like io.ReadAll except it returns 86*4882a593Smuzhiyun+// ErrFieldTooLong if more than maxSpecialFileSize is read. 87*4882a593Smuzhiyun+func readSpecialFile(r io.Reader) ([]byte, error) { 88*4882a593Smuzhiyun+ buf, err := io.ReadAll(io.LimitReader(r, maxSpecialFileSize+1)) 89*4882a593Smuzhiyun+ if len(buf) > maxSpecialFileSize { 90*4882a593Smuzhiyun+ return nil, ErrFieldTooLong 91*4882a593Smuzhiyun+ } 92*4882a593Smuzhiyun+ return buf, err 93*4882a593Smuzhiyun+} 94*4882a593Smuzhiyun+ 95*4882a593Smuzhiyun // discard skips n bytes in r, reporting an error if unable to do so. 96*4882a593Smuzhiyun func discard(r io.Reader, n int64) error { 97*4882a593Smuzhiyun // If possible, Seek to the last byte before the end of the data section. 98*4882a593Smuzhiyundiff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go 99*4882a593Smuzhiyunindex 789ddc1..926dc3d 100644 100*4882a593Smuzhiyun--- a/src/archive/tar/reader_test.go 101*4882a593Smuzhiyun+++ b/src/archive/tar/reader_test.go 102*4882a593Smuzhiyun@@ -6,6 +6,7 @@ package tar 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun import ( 105*4882a593Smuzhiyun "bytes" 106*4882a593Smuzhiyun+ "compress/bzip2" 107*4882a593Smuzhiyun "crypto/md5" 108*4882a593Smuzhiyun "errors" 109*4882a593Smuzhiyun "fmt" 110*4882a593Smuzhiyun@@ -625,9 +626,14 @@ func TestReader(t *testing.T) { 111*4882a593Smuzhiyun } 112*4882a593Smuzhiyun defer f.Close() 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun+ var fr io.Reader = f 115*4882a593Smuzhiyun+ if strings.HasSuffix(v.file, ".bz2") { 116*4882a593Smuzhiyun+ fr = bzip2.NewReader(fr) 117*4882a593Smuzhiyun+ } 118*4882a593Smuzhiyun+ 119*4882a593Smuzhiyun // Capture all headers and checksums. 120*4882a593Smuzhiyun var ( 121*4882a593Smuzhiyun- tr = NewReader(f) 122*4882a593Smuzhiyun+ tr = NewReader(fr) 123*4882a593Smuzhiyun hdrs []*Header 124*4882a593Smuzhiyun chksums []string 125*4882a593Smuzhiyun rdbuf = make([]byte, 8) 126*4882a593Smuzhiyundiff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go 127*4882a593Smuzhiyunindex e80498d..893eac0 100644 128*4882a593Smuzhiyun--- a/src/archive/tar/writer.go 129*4882a593Smuzhiyun+++ b/src/archive/tar/writer.go 130*4882a593Smuzhiyun@@ -199,6 +199,9 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { 131*4882a593Smuzhiyun flag = TypeXHeader 132*4882a593Smuzhiyun } 133*4882a593Smuzhiyun data := buf.String() 134*4882a593Smuzhiyun+ if len(data) > maxSpecialFileSize { 135*4882a593Smuzhiyun+ return ErrFieldTooLong 136*4882a593Smuzhiyun+ } 137*4882a593Smuzhiyun if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal { 138*4882a593Smuzhiyun return err // Global headers return here 139*4882a593Smuzhiyun } 140*4882a593Smuzhiyundiff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go 141*4882a593Smuzhiyunindex a00f02d..4e709e5 100644 142*4882a593Smuzhiyun--- a/src/archive/tar/writer_test.go 143*4882a593Smuzhiyun+++ b/src/archive/tar/writer_test.go 144*4882a593Smuzhiyun@@ -1006,6 +1006,33 @@ func TestIssue12594(t *testing.T) { 145*4882a593Smuzhiyun } 146*4882a593Smuzhiyun } 147*4882a593Smuzhiyun 148*4882a593Smuzhiyun+func TestWriteLongHeader(t *testing.T) { 149*4882a593Smuzhiyun+ for _, test := range []struct { 150*4882a593Smuzhiyun+ name string 151*4882a593Smuzhiyun+ h *Header 152*4882a593Smuzhiyun+ }{{ 153*4882a593Smuzhiyun+ name: "name too long", 154*4882a593Smuzhiyun+ h: &Header{Name: strings.Repeat("a", maxSpecialFileSize)}, 155*4882a593Smuzhiyun+ }, { 156*4882a593Smuzhiyun+ name: "linkname too long", 157*4882a593Smuzhiyun+ h: &Header{Linkname: strings.Repeat("a", maxSpecialFileSize)}, 158*4882a593Smuzhiyun+ }, { 159*4882a593Smuzhiyun+ name: "uname too long", 160*4882a593Smuzhiyun+ h: &Header{Uname: strings.Repeat("a", maxSpecialFileSize)}, 161*4882a593Smuzhiyun+ }, { 162*4882a593Smuzhiyun+ name: "gname too long", 163*4882a593Smuzhiyun+ h: &Header{Gname: strings.Repeat("a", maxSpecialFileSize)}, 164*4882a593Smuzhiyun+ }, { 165*4882a593Smuzhiyun+ name: "PAX header too long", 166*4882a593Smuzhiyun+ h: &Header{PAXRecords: map[string]string{"GOLANG.x": strings.Repeat("a", maxSpecialFileSize)}}, 167*4882a593Smuzhiyun+ }} { 168*4882a593Smuzhiyun+ w := NewWriter(io.Discard) 169*4882a593Smuzhiyun+ if err := w.WriteHeader(test.h); err != ErrFieldTooLong { 170*4882a593Smuzhiyun+ t.Errorf("%v: w.WriteHeader() = %v, want ErrFieldTooLong", test.name, err) 171*4882a593Smuzhiyun+ } 172*4882a593Smuzhiyun+ } 173*4882a593Smuzhiyun+} 174*4882a593Smuzhiyun+ 175*4882a593Smuzhiyun // testNonEmptyWriter wraps an io.Writer and ensures that 176*4882a593Smuzhiyun // Write is never called with an empty buffer. 177*4882a593Smuzhiyun type testNonEmptyWriter struct{ io.Writer } 178