Skip to content

Commit 7e7d55f

Browse files
committed
image/png: reject multiple tRNS chunks.
http://www.w3.org/TR/PNG/#5ChunkOrdering disallows them. Fixes #10423 Change-Id: I3399ce53dc8b41b1b5f0b906a5912e6efd80418f Reviewed-on: https://go-review.googlesource.com/8905 Reviewed-by: Rob Pike <[email protected]>
1 parent e5b7674 commit 7e7d55f

File tree

2 files changed

+71
-6
lines changed

2 files changed

+71
-6
lines changed

src/image/png/reader.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ const (
4747
cbTCA16
4848
)
4949

50+
func cbPaletted(cb int) bool {
51+
return cbP1 <= cb && cb <= cbP8
52+
}
53+
5054
// Filter type, as per the PNG spec.
5155
const (
5256
ftNone = 0
@@ -81,15 +85,16 @@ var interlacing = []interlaceScan{
8185
}
8286

8387
// Decoding stage.
84-
// The PNG specification says that the IHDR, PLTE (if present), IDAT and IEND
85-
// chunks must appear in that order. There may be multiple IDAT chunks, and
86-
// IDAT chunks must be sequential (i.e. they may not have any other chunks
87-
// between them).
88+
// The PNG specification says that the IHDR, PLTE (if present), tRNS (if
89+
// present), IDAT and IEND chunks must appear in that order. There may be
90+
// multiple IDAT chunks, and IDAT chunks must be sequential (i.e. they may not
91+
// have any other chunks between them).
8892
// http://www.w3.org/TR/PNG/#5ChunkOrdering
8993
const (
9094
dsStart = iota
9195
dsSeenIHDR
9296
dsSeenPLTE
97+
dsSeentRNS
9398
dsSeenIDAT
9499
dsSeenIEND
95100
)
@@ -687,9 +692,10 @@ func (d *decoder) parseChunk() error {
687692
if d.stage != dsSeenPLTE {
688693
return chunkOrderError
689694
}
695+
d.stage = dsSeentRNS
690696
return d.parsetRNS(length)
691697
case "IDAT":
692-
if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.cb == cbP8 && d.stage == dsSeenIHDR) {
698+
if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.stage == dsSeenIHDR && cbPaletted(d.cb)) {
693699
return chunkOrderError
694700
}
695701
d.stage = dsSeenIDAT
@@ -779,7 +785,7 @@ func DecodeConfig(r io.Reader) (image.Config, error) {
779785
}
780786
return image.Config{}, err
781787
}
782-
paletted := d.cb == cbP8 || d.cb == cbP4 || d.cb == cbP2 || d.cb == cbP1
788+
paletted := cbPaletted(d.cb)
783789
if d.stage == dsSeenIHDR && !paletted {
784790
break
785791
}

src/image/png/reader_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package png
66

77
import (
88
"bufio"
9+
"bytes"
910
"fmt"
1011
"image"
1112
"image/color"
@@ -319,6 +320,64 @@ func TestPalettedDecodeConfig(t *testing.T) {
319320
}
320321
}
321322

323+
func TestMultipletRNSChunks(t *testing.T) {
324+
/*
325+
The following is a valid 1x1 paletted PNG image with a 1-element palette
326+
containing color.NRGBA{0xff, 0x00, 0x00, 0x7f}:
327+
0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
328+
0000010: 0000 0001 0000 0001 0803 0000 0028 cb34 .............(.4
329+
0000020: bb00 0000 0350 4c54 45ff 0000 19e2 0937 .....PLTE......7
330+
0000030: 0000 0001 7452 4e53 7f80 5cb4 cb00 0000 ....tRNS..\.....
331+
0000040: 0e49 4441 5478 9c62 6200 0400 00ff ff00 .IDATx.bb.......
332+
0000050: 0600 03fa d059 ae00 0000 0049 454e 44ae .....Y.....IEND.
333+
0000060: 4260 82 B`.
334+
Dropping the tRNS chunk makes that color's alpha 0xff instead of 0x7f.
335+
*/
336+
const (
337+
ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x03\x00\x00\x00\x28\xcb\x34\xbb"
338+
plte = "\x00\x00\x00\x03PLTE\xff\x00\x00\x19\xe2\x09\x37"
339+
trns = "\x00\x00\x00\x01tRNS\x7f\x80\x5c\xb4\xcb"
340+
idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae"
341+
iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82"
342+
)
343+
for i := 0; i < 4; i++ {
344+
var b []byte
345+
b = append(b, pngHeader...)
346+
b = append(b, ihdr...)
347+
b = append(b, plte...)
348+
for j := 0; j < i; j++ {
349+
b = append(b, trns...)
350+
}
351+
b = append(b, idat...)
352+
b = append(b, iend...)
353+
354+
var want color.Color
355+
m, err := Decode(bytes.NewReader(b))
356+
switch i {
357+
case 0:
358+
if err != nil {
359+
t.Errorf("%d tRNS chunks: %v", i, err)
360+
continue
361+
}
362+
want = color.RGBA{0xff, 0x00, 0x00, 0xff}
363+
case 1:
364+
if err != nil {
365+
t.Errorf("%d tRNS chunks: %v", i, err)
366+
continue
367+
}
368+
want = color.NRGBA{0xff, 0x00, 0x00, 0x7f}
369+
default:
370+
if err == nil {
371+
t.Errorf("%d tRNS chunks: got nil error, want non-nil", i)
372+
}
373+
continue
374+
}
375+
if got := m.At(0, 0); got != want {
376+
t.Errorf("%d tRNS chunks: got %T %v, want %T %v", i, got, got, want, want)
377+
}
378+
}
379+
}
380+
322381
func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) {
323382
b.StopTimer()
324383
data, err := ioutil.ReadFile(filename)

0 commit comments

Comments
 (0)