golang/go

image/jpeg: excessive memory usage

dvyukov opened this issue · 5 comments

The following program consumes 1.3GB of memory to produce:
decode failed: invalid JPEG format: bad Huffman code
Not sure whether it is OK or not, since the format is compressed probably the image bounds are large. But still it is suspicious for the tiny input file.

package main

import (
    "bytes"
    "encoding/hex"
    "fmt"
    "image/jpeg"
)

func main() {
    data, _ := hex.DecodeString(input)
    img, err := jpeg.Decode(bytes.NewReader(data))
    if err != nil {
        fmt.Printf("decode failed: %v\n", err)
        return
    }
    fmt.Printf("bounds: %+v\n", img.Bounds())
    var w bytes.Buffer
    err = jpeg.Encode(&w, img, nil)
    if err != nil {
        panic(err)
    }
}

var input = "ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a10" +
    "0e0d0e121110131828de1816161834363125000000fe3c3c3933383440485c4e" +
    "17116df646ddaa1cd09c404457453738506d51575f626768673e4d7179706478" +
    "5c656763ffc2000b08ff80fe9501011100ffc4001a0000020301010000000000" +
    "00000000000003040102050006ffda00080101000000011badd2b348270c4c0c" +
    "3c2232968cf52a22aa801c6415211cb2ef440c4100000064a18f7f424ea33141" +
    "2f2aa1a26caaaf11bda026af5028b19365cec317116df646ddaa1cd09cc952bd" +
    "5882346354852873acc677067ae20e80236cddcb32a9427124aeac67eca68ec9" +
    "42551460c5e81674c53759f8eb03"

go version devel +87054c4 Wed Apr 22 02:50:48 2015 +0000 linux/amd64

The image bounds are large. The fragment "ffc2000b08ff80fe95" means a Start-Of-Frame marker "ffc2" whose height and width are 0xff80 and 0xfe95. In other words, the image claims to be 65173 x 65408, or over 4 billion pixels.

See also issue #5050.

Closing as WAI.

I am very sad that this issue is not taken seriously and closed as WAI. It is a real security threat and can easily be used for DoS attacks. For example, https://hackerone.com/reports/390

I understand that the API cannot distinguish between a legitimate or malicious image in this case. There should be an option to pass the max image dimensions or max memory size to prevent DoS attacks.

As it stands now, we have no way of protecting our servers against malicious images.

As it stands now, we have no way of protecting our servers against malicious images.

@roustem, yes you do: https://golang.org/pkg/image/jpeg/#DecodeConfig

Read the header to calculate its dimensions first. Use a https://golang.org/pkg/io/#TeeReader to save the bytes read into a https://golang.org/pkg/bytes/#Buffer and then if you're happy with the dimensions, use https://golang.org/pkg/io/#MultiReader to stitch together the saved header buffer with the remaining jpeg io.Reader. It all takes a few lines and is the natural Go composable way. See Camlistore's codebase for an example.

(Of course, that's only necessary if you can't rewind the stream. If it's just a file or something, you can seek back to the beginning)

That will work. Thank you, @bradfitz !