]> Cypherpunks repositories - gostls13.git/commitdiff
archive/tar: properly handle header-only "files" in Reader
authorJoe Tsai <joetsai@digital-static.net>
Wed, 4 Nov 2015 02:12:31 +0000 (18:12 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 1 Dec 2015 20:16:26 +0000 (20:16 +0000)
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 <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/archive/tar/common.go
src/archive/tar/reader.go
src/archive/tar/reader_test.go
src/archive/tar/testdata/hdr-only.tar [new file with mode: 0644]
src/archive/tar/testdata/neg-size.tar

index c31df062f7ea54577bafe6eb25dfb06ef2a2a55f..36f4e23980930c82f55749aef4e00779527ccb5c 100644 (file)
@@ -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
+       }
+}
index 4af5807b72a6cb6c0009e2b37c8ece46501819d6..2ecf39607333a0abe3a1423c723d3a5281983d8e 100644 (file)
@@ -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 = &regFileReader{r: tr.r, nb: nb}
 
        // Check for old GNU sparse format entry.
index f8b344da6e13c64c46330f8417904b5056e48d3e..51d77c168361dea78a49bc29ee5ef5d94b27cff1 100644 (file)
@@ -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 (file)
index 0000000..f250340
Binary files /dev/null and b/src/archive/tar/testdata/hdr-only.tar differ
index 5deea3d05c4da5a4ddda34ef7ad781088464e71b..21edf38cc3c3d98c834d07b6d31e8325898ec492 100644 (file)
Binary files a/src/archive/tar/testdata/neg-size.tar and b/src/archive/tar/testdata/neg-size.tar differ