VYPR
Medium severity5.3NVD Advisory· Published May 29, 2026

CVE-2026-42500

CVE-2026-42500

Description

Decoding a paletted BMP file with an out-of-range palette index results in a panic when accessing pixels in the invalid image.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A panic in the BMP decoder of golang.org/x/image when decoding a paletted BMP file with an out-of-range palette index allows denial of service via crafted image.

Vulnerability

The vulnerability exists in the BMP decoder of the golang.org/x/image library. When decoding a paletted BMP file, if the palette index in the pixel data is out of the valid range (i.e., greater than the number of palette entries), the decoder panics instead of returning an error. This affects versions prior to v0.41.0. [1][3]

Exploitation

An attacker can craft a malicious BMP file with an out-of-range palette index. No special privileges or authentication are required; the victim only needs to decode the crafted image using the vulnerable library. The decoding process triggers a panic, causing the application to crash. [1]

Impact

Successful exploitation results in a denial of service (DoS) due to the panic. The application terminates unexpectedly. No data confidentiality or integrity is compromised, but availability is affected. [1]

Mitigation

The fix was released in version v0.41.0 of golang.org/x/image on May 21, 2026. Users should update to this version or later. The decoder now correctly returns an error instead of panicking. No workarounds are mentioned. [1][3]

AI Insight generated on May 29, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
0d61147654dc

bmp: reject input with invalid palette index

https://github.com/golang/imageDamien NeilApr 22, 2026via gerrit-cl
2 files changed · +117 2
  • bmp/reader.go+9 2 modified
    @@ -18,6 +18,8 @@ import (
     // feature.
     var ErrUnsupported = errors.New("bmp: unsupported BMP image")
     
    +var errInvalidPaletteIndex = errors.New("bmp: invalid palette index")
    +
     func readUint16(b []byte) uint16 {
     	return uint16(b[0]) | uint16(b[1])<<8
     }
    @@ -29,7 +31,8 @@ func readUint32(b []byte) uint32 {
     // decodePaletted reads a 1, 2, 4 or 8 bit-per-pixel BMP image from r.
     // If topDown is false, the image rows will be read bottom-up.
     func decodePaletted(r io.Reader, c image.Config, topDown bool, bpp int) (image.Image, error) {
    -	paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), c.ColorModel.(color.Palette))
    +	palette := c.ColorModel.(color.Palette)
    +	paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), palette)
     	if c.Width == 0 || c.Height == 0 {
     		return paletted, nil
     	}
    @@ -51,7 +54,11 @@ func decodePaletted(r io.Reader, c image.Config, topDown bool, bpp int) (image.I
     		byteIndex, bitIndex, mask := 0, 8, byte((1<<bpp)-1)
     		for pixIndex := 0; pixIndex < c.Width; pixIndex++ {
     			bitIndex -= bpp
    -			p[pixIndex] = (b[byteIndex]) >> bitIndex & mask
    +			paletteIndex := (b[byteIndex]) >> bitIndex & mask
    +			if int(paletteIndex) >= len(palette) {
    +				return nil, errInvalidPaletteIndex
    +			}
    +			p[pixIndex] = paletteIndex
     			if bitIndex == 0 {
     				byteIndex++
     				bitIndex = 8
    
  • bmp/reader_test.go+108 0 modified
    @@ -6,6 +6,7 @@ package bmp
     
     import (
     	"bytes"
    +	"encoding/binary"
     	"fmt"
     	"image"
     	"io"
    @@ -83,6 +84,67 @@ func TestDecode(t *testing.T) {
     	}
     }
     
    +func TestDecodeConstructed(t *testing.T) {
    +	for _, tc := range []struct {
    +		name    string
    +		b       []byte
    +		wantErr error
    +	}{{
    +		name: "1x1 paletted",
    +		b: bmpBuilder{
    +			width:        1,
    +			height:       1,
    +			planes:       1,
    +			bitsPerPixel: 1,
    +			colorsUsed:   2,
    +			colorTable: []colorTableEntry{
    +				{0, 0, 0},
    +				{0xff, 0xff, 0xff},
    +			},
    +			data: []byte{0, 0, 0, 0},
    +		}.Bytes(),
    +		wantErr: nil, // successful base case
    +	}, {
    +		name: "1x1 rgb",
    +		b: bmpBuilder{
    +			width:        1,
    +			height:       1,
    +			planes:       1,
    +			bitsPerPixel: 24,
    +			data: []byte{
    +				0, 0, 0, 0,
    +			},
    +		}.Bytes(),
    +		wantErr: nil, // successful base case
    +	}, {
    +		name: "invalid palette index",
    +		b: bmpBuilder{
    +			width:        1,
    +			height:       1,
    +			planes:       1,
    +			bitsPerPixel: 8,
    +			colorsUsed:   2,
    +			colorTable: []colorTableEntry{
    +				{0, 0, 0},
    +				{0xff, 0xff, 0xff},
    +			},
    +			data: []byte{
    +				2, 0, 0, 0, // index 2
    +			},
    +		}.Bytes(),
    +		wantErr: errInvalidPaletteIndex,
    +	}} {
    +		img, _, err := image.Decode(bytes.NewReader(tc.b))
    +		if err != tc.wantErr {
    +			t.Errorf("%v: Decode error %v; want %v", tc.name, err, tc.wantErr)
    +		}
    +		if err != nil {
    +			continue
    +		}
    +		_ = img.At(0, 0) // try accessing a pixel
    +	}
    +}
    +
     // TestEOF tests that decoding a BMP image returns io.ErrUnexpectedEOF
     // when there are no headers or data is empty
     func TestEOF(t *testing.T) {
    @@ -91,3 +153,49 @@ func TestEOF(t *testing.T) {
     		t.Errorf("Error should be io.ErrUnexpectedEOF on nil but got %v", err)
     	}
     }
    +
    +type bmpBuilder struct {
    +	width           int32
    +	height          int32
    +	planes          uint16
    +	bitsPerPixel    uint16
    +	compression     uint32
    +	imageSize       uint32
    +	xppm            uint32
    +	yppm            uint32
    +	colorsUsed      uint32
    +	colorsImportant uint32
    +	colorTable      []colorTableEntry
    +	data            []byte
    +}
    +
    +type colorTableEntry struct {
    +	r, g, b byte
    +}
    +
    +func (b bmpBuilder) Bytes() []byte {
    +	buf := []byte{
    +		0x42, 0x4d, // 'BM'
    +		0x00, 0x00, 0x00, 0x00, // file size
    +		0x00, 0x00, 0x00, 0x00, // reserved
    +		0x00, 0x00, 0x00, 0x00, // data offset
    +		0x28, 0x00, 0x00, 0x00, // header size (40)
    +	}
    +	buf = binary.LittleEndian.AppendUint32(buf, uint32(b.width))
    +	buf = binary.LittleEndian.AppendUint32(buf, uint32(b.height))
    +	buf = binary.LittleEndian.AppendUint16(buf, b.planes)
    +	buf = binary.LittleEndian.AppendUint16(buf, b.bitsPerPixel)
    +	buf = binary.LittleEndian.AppendUint32(buf, b.compression)
    +	buf = binary.LittleEndian.AppendUint32(buf, b.imageSize)
    +	buf = binary.LittleEndian.AppendUint32(buf, b.xppm)
    +	buf = binary.LittleEndian.AppendUint32(buf, b.yppm)
    +	buf = binary.LittleEndian.AppendUint32(buf, b.colorsUsed)
    +	buf = binary.LittleEndian.AppendUint32(buf, b.colorsImportant)
    +	for _, e := range b.colorTable {
    +		buf = append(buf, e.r, e.g, e.b, 0)
    +	}
    +	binary.LittleEndian.PutUint32(buf[10:], uint32(len(buf))) // data offset
    +	buf = append(buf, b.data...)
    +	binary.LittleEndian.PutUint32(buf[2:], uint32(len(buf))) // file size
    +	return buf
    +}
    

Vulnerability mechanics

No source-code context for this CVE — mechanics is only generated when we can read the actual fix diff. Without that, the four sections (root cause, attack vector, affected code, fix) would be speculation rather than analysis.

References

4

News mentions

0

No linked articles in our index yet.