From: Nigel Tao Date: Thu, 28 Apr 2016 07:19:31 +0000 (+1000) Subject: image/gif: accept an out-of-bounds transparent color index. X-Git-Tag: go1.7beta1~416 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4618dd8704b868a095a98ee8beaf465578aaec30;p=gostls13.git image/gif: accept an out-of-bounds transparent color index. This is an error according to the spec, but Firefox and Google Chrome seem OK with this. Fixes #15059. Change-Id: I841cf44e96655e91a2481555f38fbd7055a32202 Reviewed-on: https://go-review.googlesource.com/22546 Reviewed-by: Rob Pike --- diff --git a/src/image/gif/reader.go b/src/image/gif/reader.go index 9a0852dbfd..6181a946fa 100644 --- a/src/image/gif/reader.go +++ b/src/image/gif/reader.go @@ -178,12 +178,25 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { } m.Palette = d.globalColorTable } - if d.hasTransparentIndex && int(d.transparentIndex) < len(m.Palette) { + if d.hasTransparentIndex { if !useLocalColorTable { // Clone the global color table. m.Palette = append(color.Palette(nil), d.globalColorTable...) } - m.Palette[d.transparentIndex] = color.RGBA{} + if ti := int(d.transparentIndex); ti < len(m.Palette) { + m.Palette[ti] = color.RGBA{} + } else { + // The transparentIndex is out of range, which is an error + // according to the spec, but Firefox and Google Chrome + // seem OK with this, so we enlarge the palette with + // transparent colors. See golang.org/issue/15059. + p := make(color.Palette, ti+1) + copy(p, m.Palette) + for i := len(m.Palette); i < len(p); i++ { + p[i] = color.RGBA{} + } + m.Palette = p + } } litWidth, err := d.r.ReadByte() if err != nil { diff --git a/src/image/gif/reader_test.go b/src/image/gif/reader_test.go index ee78a40716..90c81493cb 100644 --- a/src/image/gif/reader_test.go +++ b/src/image/gif/reader_test.go @@ -22,12 +22,16 @@ const ( trailerStr = "\x3b" ) -// lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes. -func lzwEncode(n int) []byte { +// lzwEncode returns an LZW encoding (with 2-bit literals) of in. +func lzwEncode(in []byte) []byte { b := &bytes.Buffer{} w := lzw.NewWriter(b, lzw.LSB, 2) - w.Write(make([]byte, n)) - w.Close() + if _, err := w.Write(in); err != nil { + panic(err) + } + if err := w.Close(); err != nil { + panic(err) + } return b.Bytes() } @@ -53,7 +57,7 @@ func TestDecode(t *testing.T) { // byte, and 2-bit LZW literals. b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") if tc.nPix > 0 { - enc := lzwEncode(tc.nPix) + enc := lzwEncode(make([]byte, tc.nPix)) if len(enc) > 0xff { t.Errorf("nPix=%d, extra=%t: compressed length %d is too large", tc.nPix, tc.extra, len(enc)) continue @@ -103,7 +107,7 @@ func TestTransparentIndex(t *testing.T) { } // Write an image with bounds 2x1, as per TestDecode. b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") - enc := lzwEncode(2) + enc := lzwEncode([]byte{0x00, 0x00}) if len(enc) > 0xff { t.Fatalf("compressed length %d is too large", len(enc)) } @@ -196,21 +200,13 @@ func TestNoPalette(t *testing.T) { b.WriteString(headerStr[:len(headerStr)-3]) b.WriteString("\x00\x00\x00") // No global palette. - // Image descriptor: 2x1, no local palette. + // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") // Encode the pixels: neither is in range, because there is no palette. - pix := []byte{0, 3} - enc := &bytes.Buffer{} - w := lzw.NewWriter(enc, lzw.LSB, 2) - if _, err := w.Write(pix); err != nil { - t.Fatalf("Write: %v", err) - } - if err := w.Close(); err != nil { - t.Fatalf("Close: %v", err) - } - b.WriteByte(byte(len(enc.Bytes()))) - b.Write(enc.Bytes()) + enc := lzwEncode([]byte{0x00, 0x03}) + b.WriteByte(byte(len(enc))) + b.Write(enc) b.WriteByte(0x00) // An empty block signifies the end of the image data. b.WriteString(trailerStr) @@ -226,21 +222,13 @@ func TestPixelOutsidePaletteRange(t *testing.T) { b.WriteString(headerStr) b.WriteString(paletteStr) - // Image descriptor: 2x1, no local palette. + // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") // Encode the pixels; some pvals trigger the expected error. - pix := []byte{pval, pval} - enc := &bytes.Buffer{} - w := lzw.NewWriter(enc, lzw.LSB, 2) - if _, err := w.Write(pix); err != nil { - t.Fatalf("Write: %v", err) - } - if err := w.Close(); err != nil { - t.Fatalf("Close: %v", err) - } - b.WriteByte(byte(len(enc.Bytes()))) - b.Write(enc.Bytes()) + enc := lzwEncode([]byte{pval, pval}) + b.WriteByte(byte(len(enc))) + b.Write(enc) b.WriteByte(0x00) // An empty block signifies the end of the image data. b.WriteString(trailerStr) @@ -254,6 +242,36 @@ func TestPixelOutsidePaletteRange(t *testing.T) { } } +func TestTransparentPixelOutsidePaletteRange(t *testing.T) { + b := &bytes.Buffer{} + + // Manufacture a GIF with a 2 color palette. + b.WriteString(headerStr) + b.WriteString(paletteStr) + + // Graphic Control Extension: transparency, transparent color index = 3. + // + // This index, 3, is out of range of the global palette and there is no + // local palette in the subsequent image descriptor. This is an error + // according to the spec, but Firefox and Google Chrome seem OK with this. + // + // See golang.org/issue/15059. + b.WriteString("\x21\xf9\x04\x01\x00\x00\x03\x00") + + // Image descriptor: 2x1, no local palette, and 2-bit LZW literals. + b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") + + // Encode the pixels. + enc := lzwEncode([]byte{0x03, 0x03}) + b.WriteByte(byte(len(enc))) + b.Write(enc) + b.WriteByte(0x00) // An empty block signifies the end of the image data. + + b.WriteString(trailerStr) + + try(t, b.Bytes(), "") +} + func TestLoopCount(t *testing.T) { data := []byte("GIF89a000\x00000,0\x00\x00\x00\n\x00" + "\n\x00\x80000000\x02\b\xf01u\xb9\xfdal\x05\x00;")