tiff: Validate palette indices when parsing palette-color images

The existing implementation will succeed to parse a corrupt or malicious
image with color indices out of range of the actual palette, which will
eventually result in a panic when the consumer tries to read the color
at any corrupted pixel.

This issue was originally discovered and filed against a downstream
library: https://github.com/disintegration/imaging/issues/165. This is
also referenced in https://osv.dev/vulnerability/GHSA-q7pp-wcgr-pffx.

Fixes golang/go#67624

Change-Id: I7d7577adb7d549ecfcd59e84e04a92d198d94c18
Reviewed-on: https://go-review.googlesource.com/c/image/+/588115
Auto-Submit: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/testdata/invalid-palette-ref.tiff b/testdata/invalid-palette-ref.tiff
new file mode 100644
index 0000000..82a7aa9
--- /dev/null
+++ b/testdata/invalid-palette-ref.tiff
Binary files differ
diff --git a/tiff/reader.go b/tiff/reader.go
index 0ad1552..1b8fcb8 100644
--- a/tiff/reader.go
+++ b/tiff/reader.go
@@ -36,7 +36,10 @@
 	return "tiff: unsupported feature: " + string(e)
 }
 
-var errNoPixels = FormatError("not enough pixel data")
+var (
+	errNoPixels          = FormatError("not enough pixel data")
+	errInvalidColorIndex = FormatError("invalid color index")
+)
 
 const maxChunkSize = 10 << 20 // 10M
 
@@ -337,13 +340,18 @@
 		}
 	case mPaletted:
 		img := dst.(*image.Paletted)
+		pLen := len(d.palette)
 		for y := ymin; y < rMaxY; y++ {
 			for x := xmin; x < rMaxX; x++ {
 				v, ok := d.readBits(d.bpp)
 				if !ok {
 					return errNoPixels
 				}
-				img.SetColorIndex(x, y, uint8(v))
+				idx := uint8(v)
+				if int(idx) >= pLen {
+					return errInvalidColorIndex
+				}
+				img.SetColorIndex(x, y, idx)
 			}
 			d.flushBits()
 		}
diff --git a/tiff/reader_test.go b/tiff/reader_test.go
index 4777fd2..0e028f6 100644
--- a/tiff/reader_test.go
+++ b/tiff/reader_test.go
@@ -414,6 +414,16 @@
 	}
 }
 
+func TestInvalidPaletteRef(t *testing.T) {
+	contents, err := ioutil.ReadFile(testdataDir + "invalid-palette-ref.tiff")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := Decode(bytes.NewReader(contents)); err == nil {
+		t.Fatal("Decode with invalid palette index: got nil error, want non-nil")
+	}
+}
+
 // benchmarkDecode benchmarks the decoding of an image.
 func benchmarkDecode(b *testing.B, filename string) {
 	b.Helper()