From dd5e14a7511465d20c6e95bf54c9b8f999abbbf6 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Tue, 3 Nov 2015 18:12:31 -0800 Subject: [PATCH] archive/tar: properly handle header-only "files" in Reader Certain special type-flags, specifically 1, 2, 3, 4, 5, 6, do not have a data section. Thus, regardless of what the size field says, we should not attempt to read any data for these special types. The relevant PAX and USTAR specification says: <<< If the typeflag field is set to specify a file to be of type 1 (a link) or 2 (a symbolic link), the size field shall be specified as zero. If the typeflag field is set to specify a file of type 5 (directory), the size field shall be interpreted as described under the definition of that record type. No data logical records are stored for types 1, 2, or 5. If the typeflag field is set to 3 (character special file), 4 (block special file), or 6 (FIFO), the meaning of the size field is unspecified by this volume of POSIX.1-2008, and no data logical records shall be stored on the medium. Additionally, for type 6, the size field shall be ignored when reading. If the typeflag field is set to any other value, the number of logical records written following the header shall be (size+511)/512, ignoring any fraction in the result of the division. >>> Contrary to the specification, we do not assert that the size field is zero for type 1 and 2 since we liberally accept non-conforming formats. Change-Id: I666b601597cb9d7a50caa081813d90ca9cfc52ed Reviewed-on: https://go-review.googlesource.com/16614 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/archive/tar/common.go | 11 +++++++ src/archive/tar/reader.go | 24 +++++++++----- src/archive/tar/reader_test.go | 43 ++++++++++++++++++++++++++ src/archive/tar/testdata/hdr-only.tar | Bin 0 -> 10240 bytes src/archive/tar/testdata/neg-size.tar | Bin 512 -> 512 bytes 5 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/archive/tar/testdata/hdr-only.tar diff --git a/src/archive/tar/common.go b/src/archive/tar/common.go index c31df062f7..36f4e23980 100644 --- a/src/archive/tar/common.go +++ b/src/archive/tar/common.go @@ -327,3 +327,14 @@ func toASCII(s string) string { } return buf.String() } + +// isHeaderOnlyType checks if the given type flag is of the type that has no +// data section even if a size is specified. +func isHeaderOnlyType(flag byte) bool { + switch flag { + case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: + return true + default: + return false + } +} diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go index 4af5807b72..2ecf396073 100644 --- a/src/archive/tar/reader.go +++ b/src/archive/tar/reader.go @@ -151,6 +151,13 @@ func (tr *Reader) Next() (*Header, error) { return nil, err } if sp != nil { + // Sparse files do not make sense when applied to the special header + // types that never have a data section. + if isHeaderOnlyType(hdr.Typeflag) { + tr.err = ErrHeader + return nil, tr.err + } + // Current file is a PAX format GNU sparse file. // Set the current file reader to a sparse file reader. tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size) @@ -539,10 +546,6 @@ func (tr *Reader) readHeader() *Header { hdr.Uid = int(tr.octal(s.next(8))) hdr.Gid = int(tr.octal(s.next(8))) hdr.Size = tr.octal(s.next(12)) - if hdr.Size < 0 { - tr.err = ErrHeader - return nil - } hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0) s.next(8) // chksum hdr.Typeflag = s.next(1)[0] @@ -593,12 +596,17 @@ func (tr *Reader) readHeader() *Header { return nil } - // Maximum value of hdr.Size is 64 GB (12 octal digits), - // so there's no risk of int64 overflowing. - nb := int64(hdr.Size) - tr.pad = -nb & (blockSize - 1) // blockSize is a power of two + nb := hdr.Size + if isHeaderOnlyType(hdr.Typeflag) { + nb = 0 + } + if nb < 0 { + tr.err = ErrHeader + return nil + } // Set the current file reader. + tr.pad = -nb & (blockSize - 1) // blockSize is a power of two tr.curr = ®FileReader{r: tr.r, nb: nb} // Check for old GNU sparse format entry. diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go index f8b344da6e..51d77c1683 100644 --- a/src/archive/tar/reader_test.go +++ b/src/archive/tar/reader_test.go @@ -901,3 +901,46 @@ func TestReadTruncation(t *testing.T) { } } } + +// TestReadHeaderOnly tests that Reader does not attempt to read special +// header-only files. +func TestReadHeaderOnly(t *testing.T) { + f, err := os.Open("testdata/hdr-only.tar") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer f.Close() + + var hdrs []*Header + tr := NewReader(f) + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Errorf("Next(): got %v, want %v", err, nil) + continue + } + hdrs = append(hdrs, hdr) + + // If a special flag, we should read nothing. + cnt, _ := io.ReadFull(tr, []byte{0}) + if cnt > 0 && hdr.Typeflag != TypeReg { + t.Errorf("ReadFull(...): got %d bytes, want 0 bytes", cnt) + } + } + + // File is crafted with 16 entries. The later 8 are identical to the first + // 8 except that the size is set. + if len(hdrs) != 16 { + t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16) + } + for i := 0; i < 8; i++ { + var hdr1, hdr2 = hdrs[i+0], hdrs[i+8] + hdr1.Size, hdr2.Size = 0, 0 + if !reflect.DeepEqual(*hdr1, *hdr2) { + t.Errorf("incorrect header:\ngot %+v\nwant %+v", *hdr1, *hdr2) + } + } +} diff --git a/src/archive/tar/testdata/hdr-only.tar b/src/archive/tar/testdata/hdr-only.tar new file mode 100644 index 0000000000000000000000000000000000000000..f25034083de6e0176e429f939875def6eb78cc73 GIT binary patch literal 10240 zcmeI2ZE}J@42Ji2Pq95gv)>o#1+ajk2rWokd-`UnK%I_CXNW`V?jLp5$;Lb+o4jM3 zRS%4K0WN2N31Pu$!vOG|0DSEi6VfBdZFVz=+!#Geo7WlKr zRl;AI>}kUnRryx%w0!65X8WAPynIb6zQg@I`q=ZhT;AVZ14uaInh{tzn(ux&A2{mb)wBl`42&RQXR%ispcL7W$7F z`oDwzV^q+8Xow$MornI@^B?pdod1LVbIgk3(>1Qxw*Nb){{{Vr0_`Z9LH`*QrhogT zdFVfV{nxJ3f3W@s{fGXsn}`0>@$ct<6aa(%Lr=2_XU@0=FB1PuCY>1poj5 literal 0 HcmV?d00001 diff --git a/src/archive/tar/testdata/neg-size.tar b/src/archive/tar/testdata/neg-size.tar index 5deea3d05c4da5a4ddda34ef7ad781088464e71b..21edf38cc3c3d98c834d07b6d31e8325898ec492 100644 GIT binary patch delta 20 bcmZo*X<(T!h11Z`)Xd0`LBU|-++;=oIaUQ| delta 20 ZcmZo*X<(T!g|mSH1ZGb%+&DLx5db<)1;zjX