Skip to content

Commit 0bf7ee9

Browse files
neildgopherbot
authored andcommitted
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 For #54853 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]> Reviewed-on: https://go-review.googlesource.com/c/go/+/439355 Reviewed-by: Damien Neil <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Auto-Submit: Roland Shoemaker <[email protected]>
1 parent c3c4aea commit 0bf7ee9

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
@@ -103,7 +103,7 @@ func (tr *Reader) next() (*Header, error) {
103103
continue // This is a meta header affecting the next header
104104
case TypeGNULongName, TypeGNULongLink:
105105
format.mayOnlyBe(FormatGNU)
106-
realname, err := io.ReadAll(tr)
106+
realname, err := readSpecialFile(tr)
107107
if err != nil {
108108
return nil, err
109109
}
@@ -293,7 +293,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) {
293293
// parsePAX parses PAX headers.
294294
// If an extended header (type 'x') is invalid, ErrHeader is returned
295295
func parsePAX(r io.Reader) (map[string]string, error) {
296-
buf, err := io.ReadAll(r)
296+
buf, err := readSpecialFile(r)
297297
if err != nil {
298298
return nil, err
299299
}
@@ -828,6 +828,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) {
828828
return n, err
829829
}
830830

831+
// readSpecialFile is like io.ReadAll except it returns
832+
// ErrFieldTooLong if more than maxSpecialFileSize is read.
833+
func readSpecialFile(r io.Reader) ([]byte, error) {
834+
buf, err := io.ReadAll(io.LimitReader(r, maxSpecialFileSize+1))
835+
if len(buf) > maxSpecialFileSize {
836+
return nil, ErrFieldTooLong
837+
}
838+
return buf, err
839+
}
840+
831841
// discard skips n bytes in r, reporting an error if unable to do so.
832842
func discard(r io.Reader, n int64) error {
833843
// 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"
@@ -243,6 +244,9 @@ func TestReader(t *testing.T) {
243244
}, {
244245
file: "testdata/pax-bad-hdr-file.tar",
245246
err: ErrHeader,
247+
}, {
248+
file: "testdata/pax-bad-hdr-large.tar.bz2",
249+
err: ErrFieldTooLong,
246250
}, {
247251
file: "testdata/pax-bad-mtime-file.tar",
248252
err: ErrHeader,
@@ -625,9 +629,14 @@ func TestReader(t *testing.T) {
625629
}
626630
defer f.Close()
627631

632+
var fr io.Reader = f
633+
if strings.HasSuffix(v.file, ".bz2") {
634+
fr = bzip2.NewReader(fr)
635+
}
636+
628637
// Capture all headers and checksums.
629638
var (
630-
tr = NewReader(f)
639+
tr = NewReader(fr)
631640
hdrs []*Header
632641
chksums []string
633642
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
@@ -1004,6 +1004,33 @@ func TestIssue12594(t *testing.T) {
10041004
}
10051005
}
10061006

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

0 commit comments

Comments
 (0)