}
func TestDimensionOverflow(t *testing.T) {
- // These bytes come from https://golang.org/issues/22304
- //
- // It encodes a 2147483646 × 2147483646 (i.e. 0x7ffffffe × 0x7ffffffe)
- // NRGBA image. The (width × height) per se doesn't overflow an int64, but
- // (width × height × bytesPerPixel) will.
- _, err := Decode(bytes.NewReader([]byte{
- 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
- 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x08, 0x06, 0x00, 0x00, 0x00, 0x30, 0x57, 0xb3,
- 0xfd, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x20, 0x12, 0x8c,
- 0x2a, 0xa4, 0xb3, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x38, 0x00, 0x15, 0x2d, 0xef,
- 0x5f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
- }))
- if _, ok := err.(UnsupportedError); !ok {
- t.Fatalf("Decode: got %v (of type %T), want non-nil error (of type png.UnsupportedError)", err, err)
+ maxInt32AsInt := int((1 << 31) - 1)
+ have32BitInts := 0 > (1 + maxInt32AsInt)
+
+ testCases := []struct {
+ src []byte
+ unsupportedConfig bool
+ width int
+ height int
+ }{
+ // These bytes come from https://golang.org/issues/22304
+ //
+ // It encodes a 2147483646 × 2147483646 (i.e. 0x7ffffffe × 0x7ffffffe)
+ // NRGBA image. The (width × height) per se doesn't overflow an int64, but
+ // (width × height × bytesPerPixel) will.
+ {
+ src: []byte{
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
+ 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x08, 0x06, 0x00, 0x00, 0x00, 0x30, 0x57, 0xb3,
+ 0xfd, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x20, 0x12, 0x8c,
+ 0x2a, 0xa4, 0xb3, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x38, 0x00, 0x15, 0x2d, 0xef,
+ 0x5f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
+ },
+ // It's debatable whether DecodeConfig (which does not allocate a
+ // pixel buffer, unlike Decode) should fail in this case. The Go
+ // standard library has made its choice, and the standard library
+ // has compatibility constraints.
+ unsupportedConfig: true,
+ width: 0x7ffffffe,
+ height: 0x7ffffffe,
+ },
+
+ // The next three cases come from https://golang.org/issues/38435
+
+ {
+ src: []byte{
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
+ 0x00, 0x00, 0xb5, 0x04, 0x00, 0x00, 0xb5, 0x04, 0x08, 0x06, 0x00, 0x00, 0x00, 0xf5, 0x60, 0x2c,
+ 0xb8, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x20, 0x12, 0x8c,
+ 0x2a, 0xa4, 0xb3, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x38, 0x00, 0x15, 0x2d, 0xef,
+ 0x5f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
+ },
+ // Here, width * height = 0x7ffea810, just under MaxInt32, but at 4
+ // bytes per pixel, the number of pixels overflows an int32.
+ unsupportedConfig: have32BitInts,
+ width: 0x0000b504,
+ height: 0x0000b504,
+ },
+
+ {
+ src: []byte{
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x30, 0x6e, 0xc5,
+ 0x21, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x20, 0x12, 0x8c,
+ 0x2a, 0xa4, 0xb3, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x38, 0x00, 0x15, 0x2d, 0xef,
+ 0x5f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
+ },
+ unsupportedConfig: false,
+ width: 0x04000000,
+ height: 0x00000001,
+ },
+
+ {
+ src: []byte{
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0xaa, 0xd4, 0x7c,
+ 0xda, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x66, 0x20, 0x12, 0x30,
+ 0x8d, 0x2a, 0xa4, 0xaf, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x14, 0xd2, 0x00, 0x16, 0x00,
+ 0x00, 0x00,
+ },
+ unsupportedConfig: false,
+ width: 0x08000000,
+ height: 0x00000001,
+ },
+ }
+
+ for i, tc := range testCases {
+ cfg, err := DecodeConfig(bytes.NewReader(tc.src))
+ if tc.unsupportedConfig {
+ if err == nil {
+ t.Errorf("i=%d: DecodeConfig: got nil error, want non-nil", i)
+ } else if _, ok := err.(UnsupportedError); !ok {
+ t.Fatalf("Decode: got %v (of type %T), want non-nil error (of type png.UnsupportedError)", err, err)
+ }
+ continue
+ } else if err != nil {
+ t.Errorf("i=%d: DecodeConfig: %v", i, err)
+ continue
+ } else if cfg.Width != tc.width {
+ t.Errorf("i=%d: width: got %d, want %d", i, cfg.Width, tc.width)
+ continue
+ } else if cfg.Height != tc.height {
+ t.Errorf("i=%d: height: got %d, want %d", i, cfg.Height, tc.height)
+ continue
+ }
+
+ if nPixels := int64(cfg.Width) * int64(cfg.Height); nPixels > 0x7f000000 {
+ // In theory, calling Decode would succeed, given several gigabytes
+ // of memory. In practice, trying to make a []uint8 big enough to
+ // hold all of the pixels can often result in OOM (out of memory).
+ // OOM is unrecoverable; we can't write a test that passes when OOM
+ // happens. Instead we skip the Decode call (and its tests).
+ continue
+ } else if testing.Short() {
+ // Even for smaller image dimensions, calling Decode might allocate
+ // 1 GiB or more of memory. This is usually feasible, and we want
+ // to check that calling Decode doesn't panic if there's enough
+ // memory, but we provide a runtime switch (testing.Short) to skip
+ // these if it would OOM. See also http://golang.org/issue/5050
+ // "decoding... images can cause huge memory allocations".
+ continue
+ }
+
+ // Even if we don't panic, these aren't valid PNG images.
+ if _, err := Decode(bytes.NewReader(tc.src)); err == nil {
+ t.Errorf("i=%d: Decode: got nil error, want non-nil", i)
+ }
+ }
+
+ if testing.Short() {
+ t.Skip("skipping tests which allocate large pixel buffers")
}
}