tiff: don't panic on reading bad ifd

Fixes golang/go#10597

Change-Id: I57b3614de7181134cf4bd0ca9dc9ff879d3d2e3d
Reviewed-on: https://go-review.googlesource.com/9414
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/tiff/reader.go b/tiff/reader.go
index 94c4cf7..3f6e397 100644
--- a/tiff/reader.go
+++ b/tiff/reader.go
@@ -71,7 +71,15 @@
 // or Long type, and returns the decoded uint values.
 func (d *decoder) ifdUint(p []byte) (u []uint, err error) {
 	var raw []byte
+	if len(p) < ifdLen {
+		return nil, FormatError("bad IFD entry")
+	}
+
 	datatype := d.byteOrder.Uint16(p[2:4])
+	if dt := int(datatype); dt <= 0 || dt >= len(lengths) {
+		return nil, UnsupportedError("IFD entry datatype")
+	}
+
 	count := d.byteOrder.Uint32(p[4:8])
 	if count > math.MaxInt32/lengths[datatype] {
 		return nil, FormatError("IFD data too large")
diff --git a/tiff/reader_test.go b/tiff/reader_test.go
index d79c9e9..1a72ac3 100644
--- a/tiff/reader_test.go
+++ b/tiff/reader_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"bytes"
+	"encoding/binary"
 	"image"
 	"io/ioutil"
 	"os"
@@ -101,6 +102,27 @@
 	}
 }
 
+func TestDecodeInvalidDataType(t *testing.T) {
+	b, err := ioutil.ReadFile("../testdata/bw-uncompressed.tiff")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// off is the offset of the ImageWidth tag. It is the offset of the overall
+	// IFD block (0x00000454), plus 2 for the uint16 number of IFD entries, plus 12
+	// to skip the first entry.
+	const off = 0x00000454 + 2 + 12*1
+
+	if v := binary.LittleEndian.Uint16(b[off : off+2]); v != tImageWidth {
+		t.Fatal(`could not find ImageWidth tag`)
+	}
+	binary.LittleEndian.PutUint16(b[off+2:], uint16(len(lengths))) // invalid datatype
+
+	if _, err = Decode(bytes.NewReader(b)); err == nil {
+		t.Fatal("got nil error, want non-nil")
+	}
+}
+
 func compare(t *testing.T, img0, img1 image.Image) {
 	b0 := img0.Bounds()
 	b1 := img1.Bounds()