diff --git a/tiff/reader.go b/tiff/reader.go index 3f6e397..ecbd474 100644 --- a/tiff/reader.go +++ b/tiff/reader.go @@ -231,7 +231,7 @@ func (d *decoder) decode(dst image.Image, xmin, ymin, xmax, ymax int) error { off += 2 } } - } else if d.bpp == 8 { + } else { var off int n := 1 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel for y := ymin; y < ymax; y++ { @@ -436,6 +436,14 @@ func newDecoder(r io.Reader) (*decoder, error) { return nil, FormatError("BitsPerSample tag missing") } d.bpp = d.firstVal(tBitsPerSample) + switch d.bpp { + case 0: + return nil, FormatError("BitsPerSample must not be 0") + case 1, 8, 16: + // Nothing to do, these are accepted by this implementation. + default: + return nil, UnsupportedError(fmt.Sprintf("BitsPerSample of %v", d.bpp)) + } // Determine the image mode. switch d.firstVal(tPhotometricInterpretation) { diff --git a/tiff/reader_test.go b/tiff/reader_test.go index 1a72ac3..f3d7128 100644 --- a/tiff/reader_test.go +++ b/tiff/reader_test.go @@ -255,12 +255,35 @@ func TestLargeIFDEntry(t *testing.T) { } } +// TestZeroBitsPerSample verifies that an IFD with a bitsPerSample of 0 does not cause a crash. +// Issue 10711. +func TestZeroBitsPerSample(t *testing.T) { + contents, err := ioutil.ReadFile(testdataDir + "bw-deflate.tiff") + if err != nil { + t.Fatal(err) + } + + // Mutate the loaded image to have the problem. + // 02 01: tag number (tBitsPerSample) + // 03 00: data type (short, or uint16) + // 01 00 00 00: count + // ?? 00 00 00: value (1 -> 0) + find := []byte{2, 1, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0} + repl := []byte{2, 1, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0} + contents = bytes.Replace(contents, find, repl, 1) + + _, err = Decode(bytes.NewReader(contents)) + if err == nil { + t.Fatal("Decode with 0 bits per sample: got nil error, want non-nil") + } +} + // benchmarkDecode benchmarks the decoding of an image. func benchmarkDecode(b *testing.B, filename string) { b.StopTimer() contents, err := ioutil.ReadFile(testdataDir + filename) if err != nil { - panic(err) + b.Fatal(err) } r := &buffer{buf: contents} b.StartTimer()