From 3bbf4a659e56fde394e7214ddd17673223aca672 Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 22 May 2024 16:46:48 -0600 Subject: [PATCH] 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 LUCI-TryBot-Result: Go LUCI Reviewed-by: Damien Neil --- testdata/invalid-palette-ref.tiff | Bin 0 -> 9662 bytes tiff/reader.go | 12 ++++++++++-- tiff/reader_test.go | 10 ++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 testdata/invalid-palette-ref.tiff diff --git a/testdata/invalid-palette-ref.tiff b/testdata/invalid-palette-ref.tiff new file mode 100644 index 0000000000000000000000000000000000000000..82a7aa9968bdaa328858080da30f7825f0e98190 GIT binary patch literal 9662 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 JfMJ4x{XduWW_bVr literal 0 HcmV?d00001 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 @@ func (e UnsupportedError) Error() string { 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 @@ func (d *decoder) decode(dst image.Image, xmin, ymin, xmax, ymax int) error { } 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 TestLargeIFDEntry(t *testing.T) { } } +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()