diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 17c1f545b7..a1de790c3d 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -131,6 +131,7 @@ protected override Image Decode(BufferedReadStream stream, Cance try { int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); + ushort bitsPerPixel = this.infoHeader.BitsPerPixel; image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); @@ -138,23 +139,27 @@ protected override Image Decode(BufferedReadStream stream, Cance switch (this.infoHeader.Compression) { - case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 32 && this.bmpMetadata.InfoHeaderType is BmpInfoHeaderType.WinVersion3: + case BmpCompression.RGB when bitsPerPixel is 32 && this.bmpMetadata.InfoHeaderType is BmpInfoHeaderType.WinVersion3: this.ReadRgb32Slow(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); break; - case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 32: + + case BmpCompression.RGB when bitsPerPixel is 32: this.ReadRgb32Fast(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); break; - case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 24: + + case BmpCompression.RGB when bitsPerPixel is 24: this.ReadRgb24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); break; - case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 16: + + case BmpCompression.RGB when bitsPerPixel is 16: this.ReadRgb16(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); break; - case BmpCompression.RGB when this.infoHeader.BitsPerPixel is <= 8 && this.processedAlphaMask: + + case BmpCompression.RGB when bitsPerPixel is > 0 and <= 8 && this.processedAlphaMask: this.ReadRgbPaletteWithAlphaMask( stream, pixels, @@ -166,7 +171,8 @@ protected override Image Decode(BufferedReadStream stream, Cance inverted); break; - case BmpCompression.RGB when this.infoHeader.BitsPerPixel is <= 8: + + case BmpCompression.RGB when bitsPerPixel is > 0 and <= 8: this.ReadRgbPalette( stream, pixels, @@ -179,6 +185,10 @@ protected override Image Decode(BufferedReadStream stream, Cance break; + case BmpCompression.RGB when bitsPerPixel is <= 0 or > 32: + BmpThrowHelper.ThrowInvalidImageContentException($"Invalid bits per pixel: {bitsPerPixel}"); + break; + case BmpCompression.RLE24: this.ReadRle24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 94cfe85ee5..6ebe1bf4e0 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -571,4 +571,27 @@ public void BmpDecoder_ThrowsException_Issue2696(TestImageProvider(ex.InnerException); } + + [Fact] + public void BmpDecoder_ThrowsException_Issue3067() + { + // Construct minimal BMP with bitsPerPixel = 0 + byte[] bmp = new byte[54]; + bmp[0] = (byte)'B'; + bmp[1] = (byte)'M'; + BitConverter.GetBytes(54).CopyTo(bmp, 2); + BitConverter.GetBytes(54).CopyTo(bmp, 10); + BitConverter.GetBytes(40).CopyTo(bmp, 14); + BitConverter.GetBytes(1).CopyTo(bmp, 18); + BitConverter.GetBytes(1).CopyTo(bmp, 22); + BitConverter.GetBytes((short)1).CopyTo(bmp, 26); + BitConverter.GetBytes((short)0).CopyTo(bmp, 28); // bitsPerPixel = 0 + + using MemoryStream stream = new(bmp); + + Assert.Throws(() => + { + using Image image = BmpDecoder.Instance.Decode(DecoderOptions.Default, stream); + }); + } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 85ff51b185..539826799e 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -204,12 +204,19 @@ public void Bpp8Test(TestImageProvider provider) } [Theory] - [WithFile(InvalidAll, PixelTypes.Rgba32)] [WithFile(InvalidBpp, PixelTypes.Rgba32)] + public void InvalidThrows_InvalidImageContentException(TestImageProvider provider) + => Assert.Throws(() => + { + using Image image = provider.GetImage(IcoDecoder.Instance); + }); + + [Theory] + [WithFile(InvalidAll, PixelTypes.Rgba32)] [WithFile(InvalidCompression, PixelTypes.Rgba32)] [WithFile(InvalidRLE4, PixelTypes.Rgba32)] [WithFile(InvalidRLE8, PixelTypes.Rgba32)] - public void InvalidTest(TestImageProvider provider) + public void InvalidThows_NotSupportedException(TestImageProvider provider) => Assert.Throws(() => { using Image image = provider.GetImage(IcoDecoder.Instance);