archive/tar: terminate when reading malformed sparse files

Fixes #10968.

Change-Id: I027bc571a71629ac49c2a0ff101b2950af6e7531
Reviewed-on: https://go-review.googlesource.com/10482
Reviewed-by: David Symonds <dsymonds@golang.org>
Run-TryBot: David Symonds <dsymonds@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index cd23fb5..ae0b97e 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -791,6 +791,9 @@
 		// Otherwise, we're at the end of the file
 		return 0, io.EOF
 	}
+	if sfr.tot < sfr.sp[0].offset {
+		return 0, io.ErrUnexpectedEOF
+	}
 	if sfr.pos < sfr.sp[0].offset {
 		// We're in a hole
 		n = sfr.readHole(b, sfr.sp[0].offset)
diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go
index ab1e844..6ffb383 100644
--- a/src/archive/tar/reader_test.go
+++ b/src/archive/tar/reader_test.go
@@ -757,3 +757,22 @@
 	}
 	io.Copy(ioutil.Discard, r)
 }
+
+// This used to hang in (*sparseFileReader).readHole due to missing
+// verification of sparse offsets against file size.
+func TestIssue10968(t *testing.T) {
+	f, err := os.Open("testdata/issue10968.tar")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer f.Close()
+	r := NewReader(f)
+	_, err = r.Next()
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = io.Copy(ioutil.Discard, r)
+	if err != io.ErrUnexpectedEOF {
+		t.Fatalf("expected %q, got %q", io.ErrUnexpectedEOF, err)
+	}
+}
diff --git a/src/archive/tar/testdata/issue10968.tar b/src/archive/tar/testdata/issue10968.tar
new file mode 100644
index 0000000..1cc837b
--- /dev/null
+++ b/src/archive/tar/testdata/issue10968.tar
Binary files differ