Skip to content

Commit

Permalink
image/gif: accept an out-of-bounds transparent color index.
Browse files Browse the repository at this point in the history
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 <r@golang.org>
  • Loading branch information
nigeltao committed Apr 29, 2016
1 parent ac0ee77 commit 4618dd8
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 32 deletions.
17 changes: 15 additions & 2 deletions src/image/gif/reader.go
Expand Up @@ -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 {
Expand Down
78 changes: 48 additions & 30 deletions src/image/gif/reader_test.go
Expand Up @@ -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()
}

Expand All @@ -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
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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;")
Expand Down

0 comments on commit 4618dd8

Please sign in to comment.