mirror of
https://github.com/golang/go.git
synced 2026-06-27 19:30:52 +00:00
image, image/gif: document DecodeConfig before Decode for untrusted input
Document that image.Decode may allocate memory proportional to width and
height from the image header before all pixel data is consumed or validated,
and that image.DecodeConfig reads only headers without allocating a full
pixel buffer.
Add package-level and function-level notes on gif.Decode, gif.DecodeAll,
and gif.DecodeConfig pointing to the same guidance and the Security
Considerations section in package image.
Add ExampleDecode_untrusted in package image: call DecodeConfig, reject
oversized dimensions using an int64 pixel product, then Decode.
Updates #79063
Change-Id: I491fa036dab49f4d413e04df161da5f430f3cf97
GitHub-Last-Rev: 8effaee716
GitHub-Pull-Request: golang/go#79221
Reviewed-on: https://go-review.googlesource.com/c/go/+/774640
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
parent
7601c4bf42
commit
c203e4ecb9
3 changed files with 73 additions and 5 deletions
|
|
@ -6,18 +6,19 @@
|
|||
package image_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"image"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
// Package image/jpeg is not used explicitly in the code below,
|
||||
// but is imported for its initialization side-effect, which allows
|
||||
// image.Decode to understand JPEG formatted images. Uncomment these
|
||||
// two lines to also understand GIF and PNG images:
|
||||
// _ "image/gif"
|
||||
// Packages image/gif and image/jpeg are not used explicitly in the code
|
||||
// below, but are imported for their initialization side-effects, which
|
||||
// allow image.Decode and image.DecodeConfig to understand GIF and JPEG
|
||||
// formatted images. Uncomment the line below to also understand PNG:
|
||||
// _ "image/png"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
)
|
||||
|
||||
|
|
@ -30,6 +31,46 @@ func Example_decodeConfig() {
|
|||
fmt.Println("Width:", config.Width, "Height:", config.Height, "Format:", format)
|
||||
}
|
||||
|
||||
// ExampleDecode_untrusted demonstrates decoding an untrusted
|
||||
// image file in two steps so that unexpectedly large
|
||||
// memory allocations can be safely avoided.
|
||||
func ExampleDecode_untrusted() {
|
||||
// This GIF data is a valid 1x1 image (layout matches package gif tests).
|
||||
gifData := []byte{
|
||||
'G', 'I', 'F', '8', '9', 'a',
|
||||
1, 0, 1, 0,
|
||||
128, 0, 0,
|
||||
0, 0, 0, 1, 1, 1,
|
||||
0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00,
|
||||
0x2c,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x01, 0x00,
|
||||
0x00,
|
||||
0x02, 0x02, 0x4c, 0x01, 0x00,
|
||||
0x3b,
|
||||
}
|
||||
|
||||
cfg, _, err := image.DecodeConfig(bytes.NewReader(gifData))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use int64 to avoid overflow on 32-bit platforms.
|
||||
const maxPixels = 10000
|
||||
if int64(cfg.Width)*int64(cfg.Height) > maxPixels {
|
||||
fmt.Println("rejected: dimensions too large")
|
||||
return
|
||||
}
|
||||
|
||||
m, _, err := image.Decode(bytes.NewReader(gifData))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("decoded", m.Bounds().Dx(), m.Bounds().Dy())
|
||||
// Output:
|
||||
// decoded 1 1
|
||||
}
|
||||
|
||||
func Example() {
|
||||
// Decode the JPEG data. If reading from file, create a reader with
|
||||
//
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ func sniff(r reader) format {
|
|||
// The string returned is the format name used during format registration.
|
||||
// Format registration is typically done by an init function in the codec-
|
||||
// specific package.
|
||||
//
|
||||
// Decoding may allocate memory proportional to the width and height in the
|
||||
// image header before all pixel data is consumed or validated. When
|
||||
// decoding untrusted input, call [DecodeConfig] first to inspect dimensions
|
||||
// and reject images that would exceed resource limits; see the "Security
|
||||
// Considerations" section in the [image] package documentation.
|
||||
func Decode(r io.Reader) (Image, string, error) {
|
||||
rr := asReader(r)
|
||||
f := sniff(rr)
|
||||
|
|
@ -98,6 +104,9 @@ func Decode(r io.Reader) (Image, string, error) {
|
|||
// been encoded in a registered format. The string returned is the format name
|
||||
// used during format registration. Format registration is typically done by
|
||||
// an init function in the codec-specific package.
|
||||
//
|
||||
// DecodeConfig reads only format headers and does not allocate a full-size
|
||||
// pixel buffer, so it can be used to check dimensions before calling [Decode].
|
||||
func DecodeConfig(r io.Reader) (Config, string, error) {
|
||||
rr := asReader(r)
|
||||
f := sniff(rr)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
// Package gif implements a GIF image decoder and encoder.
|
||||
//
|
||||
// The GIF specification is at https://www.w3.org/Graphics/GIF/spec-gif89a.txt.
|
||||
//
|
||||
// When decoding untrusted input, read dimensions with [DecodeConfig] before
|
||||
// calling [Decode] or [DecodeAll]; see those functions and the "Security
|
||||
// Considerations" section in the [image] package documentation.
|
||||
package gif
|
||||
|
||||
import (
|
||||
|
|
@ -562,6 +566,11 @@ func uninterlace(m *image.Paletted) {
|
|||
|
||||
// Decode reads a GIF image from r and returns the first embedded
|
||||
// image as an [image.Image].
|
||||
//
|
||||
// When decoding images from untrusted sources, it is safest to
|
||||
// first call DecodeConfig and check the image size so
|
||||
// that unexpectedly large memory allocations may be safely
|
||||
// avoided.
|
||||
func Decode(r io.Reader) (image.Image, error) {
|
||||
var d decoder
|
||||
if err := d.decode(r, false, false); err != nil {
|
||||
|
|
@ -602,6 +611,11 @@ type GIF struct {
|
|||
|
||||
// DecodeAll reads a GIF image from r and returns the sequential frames
|
||||
// and timing information.
|
||||
//
|
||||
// Like [Decode], this allocates a paletted buffer per frame from width and
|
||||
// height in the image descriptors. [DecodeAll] retains every decoded frame in
|
||||
// memory. For untrusted input, call [DecodeConfig] first to verify the
|
||||
// logical screen size and reject inputs that would require excessive memory.
|
||||
func DecodeAll(r io.Reader) (*GIF, error) {
|
||||
var d decoder
|
||||
if err := d.decode(r, false, true); err != nil {
|
||||
|
|
@ -624,6 +638,10 @@ func DecodeAll(r io.Reader) (*GIF, error) {
|
|||
|
||||
// DecodeConfig returns the global color model and dimensions of a GIF image
|
||||
// without decoding the entire image.
|
||||
//
|
||||
// It reads the logical screen descriptor and global color table only; it does
|
||||
// not allocate pixel buffers for frames. Use it to check width and height
|
||||
// before calling [Decode] or [DecodeAll].
|
||||
func DecodeConfig(r io.Reader) (image.Config, error) {
|
||||
var d decoder
|
||||
if err := d.decode(r, true, false); err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue