From df2f6987a5f649b913bb5013a7cc1f03a86fa2dd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 17 Oct 2023 10:19:54 +1000 Subject: [PATCH 1/4] Do not attempt to read data for chunks of length 0. --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 6 ++++++ tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs | 10 +++++++++- tests/ImageSharp.Tests/TestImages.cs | 2 ++ .../Input/Png/issues/flag_of_germany-0000016446.png | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Png/issues/flag_of_germany-0000016446.png diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 065d861e71..b14cdb122b 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -15,6 +15,7 @@ using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Memory.Internals; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; @@ -1569,6 +1570,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals [MethodImpl(InliningOptions.ShortMethod)] private IMemoryOwner ReadChunkData(int length) { + if (length == 0) + { + return new BasicArrayBuffer(Array.Empty()); + } + // We rent the buffer here to return it afterwards in Decode() IMemoryOwner buffer = this.configuration.MemoryAllocator.Allocate(length, AllocationOptions.Clean); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index e216832853..04762c3b21 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; @@ -612,4 +611,13 @@ public partial class PngDecoderTests "Disco") .Dispose(); } + + [Fact] + public void Binary_PrematureEof() + { + using EofHitCounter eofHitCounter = EofHitCounter.RunDecoder(TestImages.Png.Bad.FlagOfGermany0000016446); + + Assert.True(eofHitCounter.EofHitCount <= 2); + Assert.Equal(new Size(200, 120), eofHitCounter.Image.Size); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c5565bbd85..c33da432bf 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -169,6 +169,8 @@ public static class TestImages // Invalid color type. public const string ColorTypeOne = "Png/xc1n0g08.png"; public const string ColorTypeNine = "Png/xc9n2c08.png"; + + public const string FlagOfGermany0000016446 = "Png/issues/flag_of_germany-0000016446.png"; } } diff --git a/tests/Images/Input/Png/issues/flag_of_germany-0000016446.png b/tests/Images/Input/Png/issues/flag_of_germany-0000016446.png new file mode 100644 index 0000000000..cb8b5bc941 --- /dev/null +++ b/tests/Images/Input/Png/issues/flag_of_germany-0000016446.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64633823874512ff9f37bd321184d548816e97b8898622ae7d912b59f3099a35 +size 16446 From c7f3aa40cbee9da1c1f40651645b61b3d243aecd Mon Sep 17 00:00:00 2001 From: Jose Elias dos Santos Date: Thu, 19 Oct 2023 18:41:28 -0300 Subject: [PATCH 2/4] [fix]: Fixed Unknown App0 Marker add profileResolver missing --- .../Formats/Jpeg/Components/Decoder/JFifMarker.cs | 3 ++- .../Jpeg/Components/Decoder/ProfileResolver.cs | 8 ++++++++ .../ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs | 11 +++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Jpg/issues/issue-2564.jpg | 3 +++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Jpg/issues/issue-2564.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs index 7e25e945a5..060572fc3f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs @@ -73,7 +73,8 @@ internal readonly struct JFifMarker : IEquatable { // Some images incorrectly use JFXX as the App0 marker (Issue 2478) if (ProfileResolver.IsProfile(bytes, ProfileResolver.JFifMarker) - || ProfileResolver.IsProfile(bytes, ProfileResolver.JFxxMarker)) + || ProfileResolver.IsProfile(bytes, ProfileResolver.JFxxMarker) + || ProfileResolver.IsProfile(bytes, ProfileResolver.OSQidMaker)) { byte majorVersion = bytes[5]; byte minorVersion = bytes[6]; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs index c11679feb1..ed916c205b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs @@ -24,6 +24,14 @@ internal static class ProfileResolver (byte)'J', (byte)'F', (byte)'X', (byte)'X', (byte)'\0' }; + /// + /// Gets the \n[ID or 10 91 73 68 32 specific markers. + /// + public static ReadOnlySpan OSQidMaker => new[] + { + (byte)'\n', (byte)'[', (byte)'I', (byte)'D', (byte)' ' + }; + /// /// Gets the ICC specific markers. /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index eaa9f82cbb..f872895227 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -326,6 +326,17 @@ public partial class JpegDecoderTests image.CompareToOriginal(provider); } + // https://github.com/SixLabors/ImageSharp/discussions/2564 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2564, PixelTypes.Rgba32)] + public void Issue2564_DecodeWorks(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(JpegDecoder.Instance); + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + [Theory] [WithFile(TestImages.Jpeg.Issues.HangBadScan, PixelTypes.L8)] public void DecodeHang(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c5565bbd85..1d8cfd335b 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -291,6 +291,7 @@ public static class TestImages public const string Issue2334_NotEnoughBytesA = "Jpg/issues/issue-2334-a.jpg"; public const string Issue2334_NotEnoughBytesB = "Jpg/issues/issue-2334-b.jpg"; public const string Issue2478_JFXX = "Jpg/issues/issue-2478-jfxx.jpg"; + public const string Issue2564 = "Jpg/issues/issue-2564.jpg"; public const string HangBadScan = "Jpg/issues/Hang_C438A851.jpg"; public static class Fuzz diff --git a/tests/Images/Input/Jpg/issues/issue-2564.jpg b/tests/Images/Input/Jpg/issues/issue-2564.jpg new file mode 100644 index 0000000000..2f0b581032 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2564.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08f215c777b6fe8e315f18b7f93611c90fa86fefb8e3d37181890afb3068d8bd +size 939150 From b46ab5af846cf189fca84464a47f0b6c9c617224 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 24 Nov 2023 19:16:21 +0100 Subject: [PATCH 3/4] Explicitly set BitsPerPixel and BitsPerSample to 1 for bicolor compressed images, fixes #2587 --- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 28 +++++++++++++++++-- .../Formats/Tiff/TiffDecoderTests.cs | 6 ++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/Issues/Issue2587.tiff | 3 ++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Tiff/Issues/Issue2587.tiff diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 26905965ee..5a5c2b3e51 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -139,8 +139,8 @@ internal static class TiffDecoderOptionsParser options.OldJpegCompressionStartOfImageMarker = jpegInterchangeFormatValue.Value; } - options.ParseColorType(exifProfile); options.ParseCompression(frameMetadata.Compression, exifProfile); + options.ParseColorType(exifProfile); bool isTiled = VerifyRequiredFieldsArePresent(exifProfile, frameMetadata, options.PlanarConfiguration); @@ -194,7 +194,9 @@ internal static class TiffDecoderOptionsParser } } - if (frameMetadata.BitsPerPixel == null) + // For BiColor compressed images, the BitsPerPixel value will be set explicitly to 1, so we don't throw in those cases. + // See: https://github.com/SixLabors/ImageSharp/issues/2587 + if (frameMetadata.BitsPerPixel == null && !IsBiColorCompression(frameMetadata.Compression)) { TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!"); } @@ -570,6 +572,11 @@ internal static class TiffDecoderOptionsParser options.FaxCompressionOptions = FaxCompressionOptions.None; } + // Some encoders do not set the BitsPerSample correctly, so we set those values here to the required values: + // https://github.com/SixLabors/ImageSharp/issues/2587 + options.BitsPerSample = new TiffBitsPerSample(1, 0, 0); + options.BitsPerPixel = 1; + break; } @@ -585,12 +592,18 @@ internal static class TiffDecoderOptionsParser options.FaxCompressionOptions = FaxCompressionOptions.None; } + options.BitsPerSample = new TiffBitsPerSample(1, 0, 0); + options.BitsPerPixel = 1; + break; } case TiffCompression.Ccitt1D: { options.CompressionType = TiffDecoderCompressionType.HuffmanRle; + options.BitsPerSample = new TiffBitsPerSample(1, 0, 0); + options.BitsPerPixel = 1; + break; } @@ -645,4 +658,15 @@ internal static class TiffDecoderOptionsParser } } } + + private static bool IsBiColorCompression(TiffCompression? compression) + { + if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or + TiffCompression.CcittGroup4Fax) + { + return true; + } + + return false; + } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index decc0069a5..ab49805a35 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -665,6 +665,12 @@ public class TiffDecoderTests : TiffDecoderBaseTester public void TiffDecoder_CanDecode_TiledWithNonEqualWidthAndHeight(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + // https://github.com/SixLabors/ImageSharp/issues/2587 + [Theory] + [WithFile(Issues2587, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_BiColorWithMissingBitsPerSample(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFile(JpegCompressedGray0000539558, PixelTypes.Rgba32)] public void TiffDecoder_ThrowsException_WithCircular_IFD_Offsets(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 6ad93adfbd..1675580c02 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -992,6 +992,7 @@ public static class TestImages public const string Issues2149 = "Tiff/Issues/Group4CompressionWithStrips.tiff"; public const string Issues2255 = "Tiff/Issues/Issue2255.png"; public const string Issues2435 = "Tiff/Issues/Issue2435.tiff"; + public const string Issues2587 = "Tiff/Issues/Issue2587.tiff"; public const string JpegCompressedGray0000539558 = "Tiff/Issues/JpegCompressedGray-0000539558.tiff"; public const string Tiled0000023664 = "Tiff/Issues/tiled-0000023664.tiff"; diff --git a/tests/Images/Input/Tiff/Issues/Issue2587.tiff b/tests/Images/Input/Tiff/Issues/Issue2587.tiff new file mode 100644 index 0000000000..55368e1d3a --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue2587.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5a245e4313bcf942d9a8ab7108946ddcadcf45d898b8a8986fc42a6e8c64dc2 +size 87746 From 48cecd3c184fb686f7138257cc08517ec6b49aa4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 29 Nov 2023 17:21:22 +1000 Subject: [PATCH 4/4] Make JFIF marker optional --- .../Formats/Jpeg/Components/Decoder/JFifMarker.cs | 3 +-- .../Formats/Jpeg/Components/Decoder/ProfileResolver.cs | 8 -------- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 5 +---- tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs | 2 +- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs index 060572fc3f..7e25e945a5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs @@ -73,8 +73,7 @@ internal readonly struct JFifMarker : IEquatable { // Some images incorrectly use JFXX as the App0 marker (Issue 2478) if (ProfileResolver.IsProfile(bytes, ProfileResolver.JFifMarker) - || ProfileResolver.IsProfile(bytes, ProfileResolver.JFxxMarker) - || ProfileResolver.IsProfile(bytes, ProfileResolver.OSQidMaker)) + || ProfileResolver.IsProfile(bytes, ProfileResolver.JFxxMarker)) { byte majorVersion = bytes[5]; byte minorVersion = bytes[6]; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs index ed916c205b..c11679feb1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs @@ -24,14 +24,6 @@ internal static class ProfileResolver (byte)'J', (byte)'F', (byte)'X', (byte)'X', (byte)'\0' }; - /// - /// Gets the \n[ID or 10 91 73 68 32 specific markers. - /// - public static ReadOnlySpan OSQidMaker => new[] - { - (byte)'\n', (byte)'[', (byte)'I', (byte)'D', (byte)' ' - }; - /// /// Gets the ICC specific markers. /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 83a828caaf..ccace190f9 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -753,10 +753,7 @@ internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals Span temp = stackalloc byte[2 * 16 * 4]; stream.Read(temp, 0, JFifMarker.Length); - if (!JFifMarker.TryParse(temp, out this.jFif)) - { - JpegThrowHelper.ThrowNotSupportedException("Unknown App0 Marker - Expected JFIF."); - } + _ = JFifMarker.TryParse(temp, out this.jFif); remaining -= JFifMarker.Length; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index f872895227..deea9217f0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -330,7 +330,7 @@ public partial class JpegDecoderTests [Theory] [WithFile(TestImages.Jpeg.Issues.Issue2564, PixelTypes.Rgba32)] public void Issue2564_DecodeWorks(TestImageProvider provider) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(JpegDecoder.Instance); image.DebugSave(provider);