Commit 4618dd87 authored by Nigel Tao's avatar Nigel Tao

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/22546Reviewed-by: default avatarRob Pike <r@golang.org>
parent ac0ee77d
...@@ -178,12 +178,25 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { ...@@ -178,12 +178,25 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
} }
m.Palette = d.globalColorTable m.Palette = d.globalColorTable
} }
if d.hasTransparentIndex && int(d.transparentIndex) < len(m.Palette) { if d.hasTransparentIndex {
if !useLocalColorTable { if !useLocalColorTable {
// Clone the global color table. // Clone the global color table.
m.Palette = append(color.Palette(nil), d.globalColorTable...) 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() litWidth, err := d.r.ReadByte()
if err != nil { if err != nil {
......
...@@ -22,12 +22,16 @@ const ( ...@@ -22,12 +22,16 @@ const (
trailerStr = "\x3b" trailerStr = "\x3b"
) )
// lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes. // lzwEncode returns an LZW encoding (with 2-bit literals) of in.
func lzwEncode(n int) []byte { func lzwEncode(in []byte) []byte {
b := &bytes.Buffer{} b := &bytes.Buffer{}
w := lzw.NewWriter(b, lzw.LSB, 2) w := lzw.NewWriter(b, lzw.LSB, 2)
w.Write(make([]byte, n)) if _, err := w.Write(in); err != nil {
w.Close() panic(err)
}
if err := w.Close(); err != nil {
panic(err)
}
return b.Bytes() return b.Bytes()
} }
...@@ -53,7 +57,7 @@ func TestDecode(t *testing.T) { ...@@ -53,7 +57,7 @@ func TestDecode(t *testing.T) {
// byte, and 2-bit LZW literals. // byte, and 2-bit LZW literals.
b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
if tc.nPix > 0 { if tc.nPix > 0 {
enc := lzwEncode(tc.nPix) enc := lzwEncode(make([]byte, tc.nPix))
if len(enc) > 0xff { if len(enc) > 0xff {
t.Errorf("nPix=%d, extra=%t: compressed length %d is too large", tc.nPix, tc.extra, len(enc)) t.Errorf("nPix=%d, extra=%t: compressed length %d is too large", tc.nPix, tc.extra, len(enc))
continue continue
...@@ -103,7 +107,7 @@ func TestTransparentIndex(t *testing.T) { ...@@ -103,7 +107,7 @@ func TestTransparentIndex(t *testing.T) {
} }
// Write an image with bounds 2x1, as per TestDecode. // Write an image with bounds 2x1, as per TestDecode.
b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") 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 { if len(enc) > 0xff {
t.Fatalf("compressed length %d is too large", len(enc)) t.Fatalf("compressed length %d is too large", len(enc))
} }
...@@ -196,21 +200,13 @@ func TestNoPalette(t *testing.T) { ...@@ -196,21 +200,13 @@ func TestNoPalette(t *testing.T) {
b.WriteString(headerStr[:len(headerStr)-3]) b.WriteString(headerStr[:len(headerStr)-3])
b.WriteString("\x00\x00\x00") // No global palette. 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") 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. // Encode the pixels: neither is in range, because there is no palette.
pix := []byte{0, 3} enc := lzwEncode([]byte{0x00, 0x03})
enc := &bytes.Buffer{} b.WriteByte(byte(len(enc)))
w := lzw.NewWriter(enc, lzw.LSB, 2) b.Write(enc)
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())
b.WriteByte(0x00) // An empty block signifies the end of the image data. b.WriteByte(0x00) // An empty block signifies the end of the image data.
b.WriteString(trailerStr) b.WriteString(trailerStr)
...@@ -226,21 +222,13 @@ func TestPixelOutsidePaletteRange(t *testing.T) { ...@@ -226,21 +222,13 @@ func TestPixelOutsidePaletteRange(t *testing.T) {
b.WriteString(headerStr) b.WriteString(headerStr)
b.WriteString(paletteStr) 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") b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
// Encode the pixels; some pvals trigger the expected error. // Encode the pixels; some pvals trigger the expected error.
pix := []byte{pval, pval} enc := lzwEncode([]byte{pval, pval})
enc := &bytes.Buffer{} b.WriteByte(byte(len(enc)))
w := lzw.NewWriter(enc, lzw.LSB, 2) b.Write(enc)
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())
b.WriteByte(0x00) // An empty block signifies the end of the image data. b.WriteByte(0x00) // An empty block signifies the end of the image data.
b.WriteString(trailerStr) b.WriteString(trailerStr)
...@@ -254,6 +242,36 @@ func TestPixelOutsidePaletteRange(t *testing.T) { ...@@ -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) { func TestLoopCount(t *testing.T) {
data := []byte("GIF89a000\x00000,0\x00\x00\x00\n\x00" + data := []byte("GIF89a000\x00000,0\x00\x00\x00\n\x00" +
"\n\x00\x80000000\x02\b\xf01u\xb9\xfdal\x05\x00;") "\n\x00\x80000000\x02\b\xf01u\xb9\xfdal\x05\x00;")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment