Skip to content

Commit e429b50

Browse files
neildrcrozean
authored andcommitted
[release-branch.go1.18] archive/tar: limit size of headers
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 golang#54853 Fixes golang#55925 Change-Id: I85136d6ff1e0af101a112190e027987ab4335680 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1565555 Reviewed-by: Tatiana Bradley <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> (cherry picked from commit 6ee768cef6b82adf7a90dcf367a1699ef694f3b2) Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1590622 Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Julie Qiu <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/go/+/438500 Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Carlos Amedee <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Run-TryBot: Carlos Amedee <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 50ac9c5 commit e429b50

File tree

6 files changed

+56
-3
lines changed

6 files changed

+56
-3
lines changed

src/archive/tar/format.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ const (
143143
blockSize = 512 // Size of each block in a tar stream
144144
nameSize = 100 // Max length of the name field in USTAR format
145145
prefixSize = 155 // Max length of the prefix field in USTAR format
146+
147+
// Max length of a special file (PAX header, GNU long name or link).
148+
// This matches the limit used by libarchive.
149+
maxSpecialFileSize = 1 << 20
146150
)
147151

148152
// blockPadding computes the number of bytes needed to pad offset up to the

src/archive/tar/reader.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (tr *Reader) next() (*Header, error) {
104104
continue // This is a meta header affecting the next header
105105
case TypeGNULongName, TypeGNULongLink:
106106
format.mayOnlyBe(FormatGNU)
107-
realname, err := ioutil.ReadAll(tr)
107+
realname, err := readSpecialFile(tr)
108108
if err != nil {
109109
return nil, err
110110
}
@@ -294,7 +294,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) {
294294
// parsePAX parses PAX headers.
295295
// If an extended header (type 'x') is invalid, ErrHeader is returned
296296
func parsePAX(r io.Reader) (map[string]string, error) {
297-
buf, err := ioutil.ReadAll(r)
297+
buf, err := readSpecialFile(r)
298298
if err != nil {
299299
return nil, err
300300
}
@@ -827,6 +827,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) {
827827
return n, err
828828
}
829829

830+
// readSpecialFile is like io.ReadAll except it returns
831+
// ErrFieldTooLong if more than maxSpecialFileSize is read.
832+
func readSpecialFile(r io.Reader) ([]byte, error) {
833+
buf, err := io.ReadAll(io.LimitReader(r, maxSpecialFileSize+1))
834+
if len(buf) > maxSpecialFileSize {
835+
return nil, ErrFieldTooLong
836+
}
837+
return buf, err
838+
}
839+
830840
// discard skips n bytes in r, reporting an error if unable to do so.
831841
func discard(r io.Reader, n int64) error {
832842
// If possible, Seek to the last byte before the end of the data section.

src/archive/tar/reader_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package tar
66

77
import (
88
"bytes"
9+
"compress/bzip2"
910
"crypto/md5"
1011
"errors"
1112
"fmt"
@@ -244,6 +245,9 @@ func TestReader(t *testing.T) {
244245
}, {
245246
file: "testdata/pax-bad-hdr-file.tar",
246247
err: ErrHeader,
248+
}, {
249+
file: "testdata/pax-bad-hdr-large.tar.bz2",
250+
err: ErrFieldTooLong,
247251
}, {
248252
file: "testdata/pax-bad-mtime-file.tar",
249253
err: ErrHeader,
@@ -626,9 +630,14 @@ func TestReader(t *testing.T) {
626630
}
627631
defer f.Close()
628632

633+
var fr io.Reader = f
634+
if strings.HasSuffix(v.file, ".bz2") {
635+
fr = bzip2.NewReader(fr)
636+
}
637+
629638
// Capture all headers and checksums.
630639
var (
631-
tr = NewReader(f)
640+
tr = NewReader(fr)
632641
hdrs []*Header
633642
chksums []string
634643
rdbuf = make([]byte, 8)
156 Bytes
Binary file not shown.

src/archive/tar/writer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
199199
flag = TypeXHeader
200200
}
201201
data := buf.String()
202+
if len(data) > maxSpecialFileSize {
203+
return ErrFieldTooLong
204+
}
202205
if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal {
203206
return err // Global headers return here
204207
}

src/archive/tar/writer_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,33 @@ func TestIssue12594(t *testing.T) {
10071007
}
10081008
}
10091009

1010+
func TestWriteLongHeader(t *testing.T) {
1011+
for _, test := range []struct {
1012+
name string
1013+
h *Header
1014+
}{{
1015+
name: "name too long",
1016+
h: &Header{Name: strings.Repeat("a", maxSpecialFileSize)},
1017+
}, {
1018+
name: "linkname too long",
1019+
h: &Header{Linkname: strings.Repeat("a", maxSpecialFileSize)},
1020+
}, {
1021+
name: "uname too long",
1022+
h: &Header{Uname: strings.Repeat("a", maxSpecialFileSize)},
1023+
}, {
1024+
name: "gname too long",
1025+
h: &Header{Gname: strings.Repeat("a", maxSpecialFileSize)},
1026+
}, {
1027+
name: "PAX header too long",
1028+
h: &Header{PAXRecords: map[string]string{"GOLANG.x": strings.Repeat("a", maxSpecialFileSize)}},
1029+
}} {
1030+
w := NewWriter(io.Discard)
1031+
if err := w.WriteHeader(test.h); err != ErrFieldTooLong {
1032+
t.Errorf("%v: w.WriteHeader() = %v, want ErrFieldTooLong", test.name, err)
1033+
}
1034+
}
1035+
}
1036+
10101037
// testNonEmptyWriter wraps an io.Writer and ensures that
10111038
// Write is never called with an empty buffer.
10121039
type testNonEmptyWriter struct{ io.Writer }

0 commit comments

Comments
 (0)