summaryrefslogtreecommitdiff
path: root/src/pkg/image/gif/reader.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/image/gif/reader.go')
-rw-r--r--src/pkg/image/gif/reader.go103
1 files changed, 62 insertions, 41 deletions
diff --git a/src/pkg/image/gif/reader.go b/src/pkg/image/gif/reader.go
index 8b36948d6..8e8531f9b 100644
--- a/src/pkg/image/gif/reader.go
+++ b/src/pkg/image/gif/reader.go
@@ -17,6 +17,11 @@ import (
"io"
)
+var (
+ errNotEnough = errors.New("gif: not enough image data")
+ errTooMuch = errors.New("gif: too much image data")
+)
+
// If the io.Reader does not also have ReadByte, then decode will introduce its own buffering.
type reader interface {
io.Reader
@@ -89,29 +94,35 @@ type decoder struct {
// comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the
// reader given to the LZW decoder, which is thus immune to the
// blocking. After the LZW decoder completes, there will be a 0-byte
-// block remaining (0, ()), but under normal execution blockReader
-// doesn't consume it, so it is handled in decode.
+// block remaining (0, ()), which is consumed when checking that the
+// blockReader is exhausted.
type blockReader struct {
r reader
slice []byte
+ err error
tmp [256]byte
}
func (b *blockReader) Read(p []byte) (int, error) {
+ if b.err != nil {
+ return 0, b.err
+ }
if len(p) == 0 {
return 0, nil
}
if len(b.slice) == 0 {
- blockLen, err := b.r.ReadByte()
- if err != nil {
- return 0, err
+ var blockLen uint8
+ blockLen, b.err = b.r.ReadByte()
+ if b.err != nil {
+ return 0, b.err
}
if blockLen == 0 {
- return 0, io.EOF
+ b.err = io.EOF
+ return 0, b.err
}
b.slice = b.tmp[0:blockLen]
- if _, err = io.ReadFull(b.r, b.slice); err != nil {
- return 0, err
+ if _, b.err = io.ReadFull(b.r, b.slice); b.err != nil {
+ return 0, b.err
}
}
n := copy(p, b.slice)
@@ -142,35 +153,33 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
}
}
-Loop:
- for err == nil {
- var c byte
- c, err = d.r.ReadByte()
- if err == io.EOF {
- break
+ for {
+ c, err := d.r.ReadByte()
+ if err != nil {
+ return err
}
switch c {
case sExtension:
- err = d.readExtension()
+ if err = d.readExtension(); err != nil {
+ return err
+ }
case sImageDescriptor:
- var m *image.Paletted
- m, err = d.newImageFromDescriptor()
+ m, err := d.newImageFromDescriptor()
if err != nil {
- break
+ return err
}
if d.imageFields&fColorMapFollows != 0 {
m.Palette, err = d.readColorMap()
if err != nil {
- break
+ return err
}
// TODO: do we set transparency in this map too? That would be
// d.setTransparency(m.Palette)
} else {
m.Palette = d.globalColorMap
}
- var litWidth uint8
- litWidth, err = d.r.ReadByte()
+ litWidth, err := d.r.ReadByte()
if err != nil {
return err
}
@@ -178,18 +187,27 @@ Loop:
return fmt.Errorf("gif: pixel size in decode out of range: %d", litWidth)
}
// A wonderfully Go-like piece of magic.
- lzwr := lzw.NewReader(&blockReader{r: d.r}, lzw.LSB, int(litWidth))
+ br := &blockReader{r: d.r}
+ lzwr := lzw.NewReader(br, lzw.LSB, int(litWidth))
if _, err = io.ReadFull(lzwr, m.Pix); err != nil {
- break
+ if err != io.ErrUnexpectedEOF {
+ return err
+ }
+ return errNotEnough
}
-
- // There should be a "0" block remaining; drain that.
- c, err = d.r.ReadByte()
- if err != nil {
- return err
+ // Both lzwr and br should be exhausted. Reading from them
+ // should yield (0, io.EOF).
+ if n, err := lzwr.Read(d.tmp[:1]); n != 0 || err != io.EOF {
+ if err != nil {
+ return err
+ }
+ return errTooMuch
}
- if c != 0 {
- return errors.New("gif: extra data after image")
+ if n, err := br.Read(d.tmp[:1]); n != 0 || err != io.EOF {
+ if err != nil {
+ return err
+ }
+ return errTooMuch
}
// Undo the interlacing if necessary.
@@ -202,19 +220,15 @@ Loop:
d.delayTime = 0 // TODO: is this correct, or should we hold on to the value?
case sTrailer:
- break Loop
+ if len(d.image) == 0 {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
default:
- err = fmt.Errorf("gif: unknown block type: 0x%.2x", c)
+ return fmt.Errorf("gif: unknown block type: 0x%.2x", c)
}
}
- if err != nil {
- return err
- }
- if len(d.image) == 0 {
- return io.ErrUnexpectedEOF
- }
- return nil
}
func (d *decoder) readHeaderAndScreenDescriptor() error {
@@ -304,7 +318,6 @@ func (d *decoder) readExtension() error {
return err
}
}
- panic("unreachable")
}
func (d *decoder) readGraphicControl() error {
@@ -335,7 +348,15 @@ func (d *decoder) newImageFromDescriptor() (*image.Paletted, error) {
width := int(d.tmp[4]) + int(d.tmp[5])<<8
height := int(d.tmp[6]) + int(d.tmp[7])<<8
d.imageFields = d.tmp[8]
- return image.NewPaletted(image.Rect(left, top, left+width, top+height), nil), nil
+
+ // The GIF89a spec, Section 20 (Image Descriptor) says:
+ // "Each image must fit within the boundaries of the Logical
+ // Screen, as defined in the Logical Screen Descriptor."
+ bounds := image.Rect(left, top, left+width, top+height)
+ if bounds != bounds.Intersect(image.Rect(0, 0, d.width, d.height)) {
+ return nil, errors.New("gif: frame bounds larger than image bounds")
+ }
+ return image.NewPaletted(bounds, nil), nil
}
func (d *decoder) readBlock() (int, error) {