diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 4b68c39ea9..73af42afd4 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -472,7 +472,7 @@ private JpegColorSpace DeduceJpegColorSpace(byte componentCount) : JpegColorSpace.Cmyk; } - JpegThrowHelper.ThrowInvalidImageContentException($"Unsupported color mode. Supported component counts 1, 3, and 4; found {componentCount}"); + JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount); return default; } @@ -998,6 +998,14 @@ private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining, // 1 byte: Number of components byte componentCount = this.temp[5]; + // Validate: componentCount more than 4 can lead to a buffer overflow during stream + // reading so we must limit it to 4 + // We do not support jpeg images with more than 4 components anyway + if (componentCount > 4) + { + JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount); + } + this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount); remaining -= length; diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs index 0292fbcab4..b238e45ef3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs @@ -45,5 +45,8 @@ internal static class JpegThrowHelper [MethodImpl(InliningOptions.ColdPath)] public static void ThrowDimensionsTooLarge(int width, int height) => throw new ImageFormatException($"Image is too large to encode at {width}x{height} for JPEG format."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNotSupportedComponentCount(int componentCount) => throw new NotSupportedException($"Images with {componentCount} components are not supported."); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index d82359e61a..db5169a045 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -10,58 +10,72 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public partial class JpegDecoderTests { public static string[] BaselineTestJpegs = - { - TestImages.Jpeg.Baseline.Calliphora, - TestImages.Jpeg.Baseline.Cmyk, - TestImages.Jpeg.Baseline.Ycck, - TestImages.Jpeg.Baseline.Jpeg400, - TestImages.Jpeg.Baseline.Turtle420, - TestImages.Jpeg.Baseline.Testorig420, - TestImages.Jpeg.Baseline.Jpeg420Small, - TestImages.Jpeg.Issues.Fuzz.AccessViolationException922, - TestImages.Jpeg.Baseline.Jpeg444, - TestImages.Jpeg.Baseline.Jpeg422, - TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, - TestImages.Jpeg.Baseline.YcckSubsample1222, - TestImages.Jpeg.Baseline.Bad.BadRST, - TestImages.Jpeg.Issues.MultiHuffmanBaseline394, - TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, - TestImages.Jpeg.Issues.InvalidEOI695, - TestImages.Jpeg.Issues.ExifResizeOutOfRange696, - TestImages.Jpeg.Issues.InvalidAPP0721, - TestImages.Jpeg.Issues.ExifGetString750Load, - TestImages.Jpeg.Issues.ExifGetString750Transform, - TestImages.Jpeg.Issues.BadSubSampling1076, + { + TestImages.Jpeg.Baseline.Calliphora, + TestImages.Jpeg.Baseline.Cmyk, + TestImages.Jpeg.Baseline.Ycck, + TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Turtle420, + TestImages.Jpeg.Baseline.Testorig420, + TestImages.Jpeg.Baseline.Jpeg420Small, + TestImages.Jpeg.Issues.Fuzz.AccessViolationException922, + TestImages.Jpeg.Baseline.Jpeg444, + TestImages.Jpeg.Baseline.Jpeg422, + TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, + TestImages.Jpeg.Baseline.YcckSubsample1222, + TestImages.Jpeg.Baseline.Bad.BadRST, + TestImages.Jpeg.Issues.MultiHuffmanBaseline394, + TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, + TestImages.Jpeg.Issues.InvalidEOI695, + TestImages.Jpeg.Issues.ExifResizeOutOfRange696, + TestImages.Jpeg.Issues.InvalidAPP0721, + TestImages.Jpeg.Issues.ExifGetString750Load, + TestImages.Jpeg.Issues.ExifGetString750Transform, + TestImages.Jpeg.Issues.BadSubSampling1076, - // LibJpeg can open this despite the invalid density units. - TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B, + // LibJpeg can open this despite the invalid density units. + TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B, - // LibJpeg can open this despite incorrect colorspace metadata. - TestImages.Jpeg.Issues.IncorrectColorspace855, + // LibJpeg can open this despite incorrect colorspace metadata. + TestImages.Jpeg.Issues.IncorrectColorspace855, - // High depth images - TestImages.Jpeg.Baseline.Testorig12bit, - }; + // High depth images + TestImages.Jpeg.Baseline.Testorig12bit, + }; public static string[] ProgressiveTestJpegs = - { - TestImages.Jpeg.Progressive.Fb, - TestImages.Jpeg.Progressive.Progress, - TestImages.Jpeg.Progressive.Festzug, - TestImages.Jpeg.Progressive.Bad.BadEOF, - TestImages.Jpeg.Issues.BadCoeffsProgressive178, - TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159, - TestImages.Jpeg.Issues.MissingFF00ProgressiveBedroom159, - TestImages.Jpeg.Issues.BadZigZagProgressive385, - TestImages.Jpeg.Progressive.Bad.ExifUndefType, - TestImages.Jpeg.Issues.NoEoiProgressive517, - TestImages.Jpeg.Issues.BadRstProgressive518, - TestImages.Jpeg.Issues.DhtHasWrongLength624, - TestImages.Jpeg.Issues.OrderedInterleavedProgressive723A, - TestImages.Jpeg.Issues.OrderedInterleavedProgressive723B, - TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C - }; + { + TestImages.Jpeg.Progressive.Fb, + TestImages.Jpeg.Progressive.Progress, + TestImages.Jpeg.Progressive.Festzug, + TestImages.Jpeg.Progressive.Bad.BadEOF, + TestImages.Jpeg.Issues.BadCoeffsProgressive178, + TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159, + TestImages.Jpeg.Issues.MissingFF00ProgressiveBedroom159, + TestImages.Jpeg.Issues.BadZigZagProgressive385, + TestImages.Jpeg.Progressive.Bad.ExifUndefType, + TestImages.Jpeg.Issues.NoEoiProgressive517, + TestImages.Jpeg.Issues.BadRstProgressive518, + TestImages.Jpeg.Issues.DhtHasWrongLength624, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723A, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723B, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C + }; + + public static string[] UnsupportedTestJpegs = + { + // Invalid componentCount value (2 or > 4) + TestImages.Jpeg.Issues.Fuzz.NullReferenceException823, + TestImages.Jpeg.Issues.MalformedUnsupportedComponentCount, + + // Arithmetic coding + TestImages.Jpeg.Baseline.ArithmeticCoding, + TestImages.Jpeg.Baseline.ArithmeticCodingProgressive, + + // Lossless jpeg + TestImages.Jpeg.Baseline.Lossless + }; public static string[] UnrecoverableTestJpegs = { @@ -70,7 +84,6 @@ public partial class JpegDecoderTests TestImages.Jpeg.Issues.Fuzz.AccessViolationException798, TestImages.Jpeg.Issues.Fuzz.DivideByZeroException821, TestImages.Jpeg.Issues.Fuzz.DivideByZeroException822, - TestImages.Jpeg.Issues.Fuzz.NullReferenceException823, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824A, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824B, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824D, @@ -91,28 +104,27 @@ public partial class JpegDecoderTests TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C, }; - private static readonly Dictionary CustomToleranceValues = - new Dictionary - { - // Baseline: - [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, - [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, - [TestImages.Jpeg.Baseline.Bad.BadRST] = 0.0589f / 100, + private static readonly Dictionary CustomToleranceValues = new() + { + // Baseline: + [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, + [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, + [TestImages.Jpeg.Baseline.Bad.BadRST] = 0.0589f / 100, - [TestImages.Jpeg.Baseline.Jpeg422] = 0.0013f / 100, - [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, - [TestImages.Jpeg.Baseline.Jpeg420Small] = 0.287f / 100, - [TestImages.Jpeg.Baseline.Turtle420] = 1.0f / 100, + [TestImages.Jpeg.Baseline.Jpeg422] = 0.0013f / 100, + [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, + [TestImages.Jpeg.Baseline.Jpeg420Small] = 0.287f / 100, + [TestImages.Jpeg.Baseline.Turtle420] = 1.0f / 100, - // Progressive: - [TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159] = 0.34f / 100, - [TestImages.Jpeg.Issues.BadCoeffsProgressive178] = 0.38f / 100, - [TestImages.Jpeg.Progressive.Bad.BadEOF] = 0.3f / 100, - [TestImages.Jpeg.Progressive.Festzug] = 0.02f / 100, - [TestImages.Jpeg.Progressive.Fb] = 0.16f / 100, - [TestImages.Jpeg.Progressive.Progress] = 0.31f / 100, - [TestImages.Jpeg.Issues.BadZigZagProgressive385] = 0.23f / 100, - [TestImages.Jpeg.Progressive.Bad.ExifUndefType] = 0.011f / 100, - }; + // Progressive: + [TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159] = 0.34f / 100, + [TestImages.Jpeg.Issues.BadCoeffsProgressive178] = 0.38f / 100, + [TestImages.Jpeg.Progressive.Bad.BadEOF] = 0.3f / 100, + [TestImages.Jpeg.Progressive.Festzug] = 0.02f / 100, + [TestImages.Jpeg.Progressive.Fb] = 0.16f / 100, + [TestImages.Jpeg.Progressive.Progress] = 0.31f / 100, + [TestImages.Jpeg.Issues.BadZigZagProgressive385] = 0.23f / 100, + [TestImages.Jpeg.Progressive.Bad.ExifUndefType] = 0.011f / 100, + }; } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 08d8d90382..7a24469597 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -190,9 +190,7 @@ public async Task Identify_IsCancellable() } [Theory] - [WithFile(TestImages.Jpeg.Baseline.ArithmeticCoding, PixelTypes.Rgba32)] - [WithFile(TestImages.Jpeg.Baseline.ArithmeticCodingProgressive, PixelTypes.Rgba32)] - [WithFile(TestImages.Jpeg.Baseline.Lossless, PixelTypes.Rgba32)] + [WithFileCollection(nameof(UnsupportedTestJpegs), PixelTypes.Rgba32)] public void ThrowsNotSupported_WithUnsupportedJpegs(TestImageProvider provider) where TPixel : unmanaged, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 3f28a61160..deed0b2404 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -256,6 +256,7 @@ public static class Issues public const string BadSubSampling1076 = "Jpg/issues/issue-1076-invalid-subsampling.jpg"; public const string IdentifyMultiFrame1211 = "Jpg/issues/issue-1221-identify-multi-frame.jpg"; public const string WrongColorSpace = "Jpg/issues/Issue1732-WrongColorSpace.jpg"; + public const string MalformedUnsupportedComponentCount = "Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg b/tests/Images/Input/Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg new file mode 100644 index 0000000000..7b6d7c5728 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3553f76b72b98986df13a7508cd073a30b7c846dc441d1d68a518bf7b93bc66 +size 3951