tiff/lzw: avoid hi code overflow This is the identical fix from CL 42032 (compress/lzw) applied to the tiff lzw reader. Fixes golang/go#81125 Change-Id: Ie2b1fbc88608c3d778224c72d19564d16a6a6964 Reviewed-on: https://go-review.googlesource.com/c/image/+/821880 Reviewed-by: Nicholas Husin <nsh@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <husin@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
diff --git a/testdata/lzw-hi-overflow.tiff.bz2 b/testdata/lzw-hi-overflow.tiff.bz2 new file mode 100644 index 0000000..8fa9ebb --- /dev/null +++ b/testdata/lzw-hi-overflow.tiff.bz2 Binary files differ
diff --git a/tiff/lzw/reader.go b/tiff/lzw/reader.go index 0aae475..149b905 100644 --- a/tiff/lzw/reader.go +++ b/tiff/lzw/reader.go
@@ -211,6 +211,10 @@ if d.hi+1 >= d.overflow { // NOTE: the "+1" is where TIFF's LZW differs from the standard algorithm. if d.width == maxWidth { d.last = decoderInvalidCode + // Undo the d.hi++ a few lines above, so that (1) we maintain + // the invariant that d.hi < d.overflow, and (2) d.hi does not + // eventually overflow a uint16. + d.hi-- } else { d.width++ d.overflow <<= 1
diff --git a/tiff/reader_test.go b/tiff/reader_test.go index 8bbce2b..3fa8fb2 100644 --- a/tiff/reader_test.go +++ b/tiff/reader_test.go
@@ -36,7 +36,11 @@ return nil, err } defer f.Close() - img, _, err := image.Decode(f) + var r io.Reader = f + if strings.HasSuffix(name, ".bz2") { + r = bzip2.NewReader(f) + } + img, _, err := image.Decode(r) if err != nil { return nil, err } @@ -428,6 +432,27 @@ } } +// TestLZWHiCodeOverflow tests decoding a 256x256 black image +// which used to cause a panic due to the decoder's hi code overflowing the code table. +func TestLZWHiCodeOverflow(t *testing.T) { + img, err := load("lzw-hi-overflow.tiff.bz2") + if err != nil { + t.Fatal(err) + } + g, ok := img.(*image.Gray) + if !ok { + t.Fatalf("got %T, want *image.Gray", img) + } + if got, want := g.Bounds(), image.Rect(0, 0, 256, 256); got != want { + t.Fatalf("bounds: got %v, want %v", got, want) + } + for i, v := range g.Pix { + if v != 0 { + t.Fatalf("pixel %d: got %d, want 0", i, v) + } + } +} + // benchmarkDecode benchmarks the decoding of an image. func benchmarkDecode(b *testing.B, filename string) { b.Helper()