Skip to content

Commit 4618dd8

Browse files
committed
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 <[email protected]>
1 parent ac0ee77 commit 4618dd8

File tree

2 files changed

+63
-32
lines changed

2 files changed

+63
-32
lines changed

src/image/gif/reader.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,25 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
178178
}
179179
m.Palette = d.globalColorTable
180180
}
181-
if d.hasTransparentIndex && int(d.transparentIndex) < len(m.Palette) {
181+
if d.hasTransparentIndex {
182182
if !useLocalColorTable {
183183
// Clone the global color table.
184184
m.Palette = append(color.Palette(nil), d.globalColorTable...)
185185
}
186-
m.Palette[d.transparentIndex] = color.RGBA{}
186+
if ti := int(d.transparentIndex); ti < len(m.Palette) {
187+
m.Palette[ti] = color.RGBA{}
188+
} else {
189+
// The transparentIndex is out of range, which is an error
190+
// according to the spec, but Firefox and Google Chrome
191+
// seem OK with this, so we enlarge the palette with
192+
// transparent colors. See golang.org/issue/15059.
193+
p := make(color.Palette, ti+1)
194+
copy(p, m.Palette)
195+
for i := len(m.Palette); i < len(p); i++ {
196+
p[i] = color.RGBA{}
197+
}
198+
m.Palette = p
199+
}
187200
}
188201
litWidth, err := d.r.ReadByte()
189202
if err != nil {

src/image/gif/reader_test.go

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ const (
2222
trailerStr = "\x3b"
2323
)
2424

25-
// lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes.
26-
func lzwEncode(n int) []byte {
25+
// lzwEncode returns an LZW encoding (with 2-bit literals) of in.
26+
func lzwEncode(in []byte) []byte {
2727
b := &bytes.Buffer{}
2828
w := lzw.NewWriter(b, lzw.LSB, 2)
29-
w.Write(make([]byte, n))
30-
w.Close()
29+
if _, err := w.Write(in); err != nil {
30+
panic(err)
31+
}
32+
if err := w.Close(); err != nil {
33+
panic(err)
34+
}
3135
return b.Bytes()
3236
}
3337

@@ -53,7 +57,7 @@ func TestDecode(t *testing.T) {
5357
// byte, and 2-bit LZW literals.
5458
b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
5559
if tc.nPix > 0 {
56-
enc := lzwEncode(tc.nPix)
60+
enc := lzwEncode(make([]byte, tc.nPix))
5761
if len(enc) > 0xff {
5862
t.Errorf("nPix=%d, extra=%t: compressed length %d is too large", tc.nPix, tc.extra, len(enc))
5963
continue
@@ -103,7 +107,7 @@ func TestTransparentIndex(t *testing.T) {
103107
}
104108
// Write an image with bounds 2x1, as per TestDecode.
105109
b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
106-
enc := lzwEncode(2)
110+
enc := lzwEncode([]byte{0x00, 0x00})
107111
if len(enc) > 0xff {
108112
t.Fatalf("compressed length %d is too large", len(enc))
109113
}
@@ -196,21 +200,13 @@ func TestNoPalette(t *testing.T) {
196200
b.WriteString(headerStr[:len(headerStr)-3])
197201
b.WriteString("\x00\x00\x00") // No global palette.
198202

199-
// Image descriptor: 2x1, no local palette.
203+
// Image descriptor: 2x1, no local palette, and 2-bit LZW literals.
200204
b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
201205

202206
// Encode the pixels: neither is in range, because there is no palette.
203-
pix := []byte{0, 3}
204-
enc := &bytes.Buffer{}
205-
w := lzw.NewWriter(enc, lzw.LSB, 2)
206-
if _, err := w.Write(pix); err != nil {
207-
t.Fatalf("Write: %v", err)
208-
}
209-
if err := w.Close(); err != nil {
210-
t.Fatalf("Close: %v", err)
211-
}
212-
b.WriteByte(byte(len(enc.Bytes())))
213-
b.Write(enc.Bytes())
207+
enc := lzwEncode([]byte{0x00, 0x03})
208+
b.WriteByte(byte(len(enc)))
209+
b.Write(enc)
214210
b.WriteByte(0x00) // An empty block signifies the end of the image data.
215211

216212
b.WriteString(trailerStr)
@@ -226,21 +222,13 @@ func TestPixelOutsidePaletteRange(t *testing.T) {
226222
b.WriteString(headerStr)
227223
b.WriteString(paletteStr)
228224

229-
// Image descriptor: 2x1, no local palette.
225+
// Image descriptor: 2x1, no local palette, and 2-bit LZW literals.
230226
b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
231227

232228
// Encode the pixels; some pvals trigger the expected error.
233-
pix := []byte{pval, pval}
234-
enc := &bytes.Buffer{}
235-
w := lzw.NewWriter(enc, lzw.LSB, 2)
236-
if _, err := w.Write(pix); err != nil {
237-
t.Fatalf("Write: %v", err)
238-
}
239-
if err := w.Close(); err != nil {
240-
t.Fatalf("Close: %v", err)
241-
}
242-
b.WriteByte(byte(len(enc.Bytes())))
243-
b.Write(enc.Bytes())
229+
enc := lzwEncode([]byte{pval, pval})
230+
b.WriteByte(byte(len(enc)))
231+
b.Write(enc)
244232
b.WriteByte(0x00) // An empty block signifies the end of the image data.
245233

246234
b.WriteString(trailerStr)
@@ -254,6 +242,36 @@ func TestPixelOutsidePaletteRange(t *testing.T) {
254242
}
255243
}
256244

245+
func TestTransparentPixelOutsidePaletteRange(t *testing.T) {
246+
b := &bytes.Buffer{}
247+
248+
// Manufacture a GIF with a 2 color palette.
249+
b.WriteString(headerStr)
250+
b.WriteString(paletteStr)
251+
252+
// Graphic Control Extension: transparency, transparent color index = 3.
253+
//
254+
// This index, 3, is out of range of the global palette and there is no
255+
// local palette in the subsequent image descriptor. This is an error
256+
// according to the spec, but Firefox and Google Chrome seem OK with this.
257+
//
258+
// See golang.org/issue/15059.
259+
b.WriteString("\x21\xf9\x04\x01\x00\x00\x03\x00")
260+
261+
// Image descriptor: 2x1, no local palette, and 2-bit LZW literals.
262+
b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02")
263+
264+
// Encode the pixels.
265+
enc := lzwEncode([]byte{0x03, 0x03})
266+
b.WriteByte(byte(len(enc)))
267+
b.Write(enc)
268+
b.WriteByte(0x00) // An empty block signifies the end of the image data.
269+
270+
b.WriteString(trailerStr)
271+
272+
try(t, b.Bytes(), "")
273+
}
274+
257275
func TestLoopCount(t *testing.T) {
258276
data := []byte("GIF89a000\x00000,0\x00\x00\x00\n\x00" +
259277
"\n\x00\x80000000\x02\b\xf01u\xb9\xfdal\x05\x00;")

0 commit comments

Comments
 (0)