From 4387db97f5ce1ceaa799565e9544a3012d0085f2 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 31 May 2024 20:11:51 +0200 Subject: [PATCH 1/4] Limit lzw bits to a maximum of 12 bits, fixes issue #2743 --- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 9 +++++++-- .../Formats/Gif/GifDecoderTests.cs | 17 +++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Gif/issues/issue_2743.gif | 3 +++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Gif/issues/issue_2743.gif diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 4d282f26c6..1a05178fdb 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -19,6 +19,11 @@ internal sealed class LzwDecoder : IDisposable /// private const int MaxStackSize = 4096; + /// + /// The maximum bits for a lzw code. + /// + private const int MaximumLzwBits = 12; + /// /// The null code. /// @@ -73,7 +78,7 @@ public void DecodePixels(int minCodeSize, Buffer2D pixels) // It is possible to specify a larger LZW minimum code size than the palette length in bits // which may leave a gap in the codes where no colors are assigned. // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression - if (minCodeSize < 2 || clearCode > MaxStackSize) + if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) { // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided @@ -245,7 +250,7 @@ public void SkipIndices(int minCodeSize, int length) // It is possible to specify a larger LZW minimum code size than the palette length in bits // which may leave a gap in the codes where no colors are assigned. // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression - if (minCodeSize < 2 || clearCode > MaxStackSize) + if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) { // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 6399e1193e..7aabe03481 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -318,4 +318,21 @@ public void IssueDeferredClearCode(TestImageProvider provider) image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); } + + // https://github.com/SixLabors/ImageSharp/issues/2743 + [Theory] + [WithFile(TestImages.Gif.Issues.BadMaxLzwBits, PixelTypes.Rgba32)] + public void IssueTooLargeLzwBits(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + Exception ex = Record.Exception( + () => + { + using Image image = provider.GetImage(); + image.DebugSave(provider); + }); + + Assert.NotNull(ex); + Assert.Contains("Gif Image does not contain a valid LZW minimum code.", ex.Message); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index a45931e29e..7aaaac6f81 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -516,6 +516,7 @@ public static class Issues public const string BadAppExtLength = "Gif/issues/issue405_badappextlength252.gif"; public const string BadAppExtLength_2 = "Gif/issues/issue405_badappextlength252-2.gif"; public const string BadDescriptorWidth = "Gif/issues/issue403_baddescriptorwidth.gif"; + public const string BadMaxLzwBits = "Gif/issues/issue_2743.gif"; public const string DeferredClearCode = "Gif/issues/bugzilla-55918.gif"; public const string Issue1505 = "Gif/issues/issue1505_argumentoutofrange.png"; public const string Issue1530 = "Gif/issues/issue1530.gif"; diff --git a/tests/Images/Input/Gif/issues/issue_2743.gif b/tests/Images/Input/Gif/issues/issue_2743.gif new file mode 100644 index 0000000000..4ce61340d9 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2743.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4be51cb9c258a6518d791ad2810fa0d71449805a5d5a8f95dcc7da2dc558ed73 +size 166413 From 35ea961552dc9a827c6b6119d9ba26b1dd8292fc Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 2 Jun 2024 12:49:21 +0200 Subject: [PATCH 2/4] Dispose currentLocalColorTable in final block --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index d64792eba7..487457fbff 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -463,6 +463,7 @@ private void ReadFrame(BufferedReadStream stream, ref Image? ima finally { indices?.Dispose(); + this.currentLocalColorTable?.Dispose(); } } From eace209d5f887c1e403ae539145a0fb9e6462f2e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 2 Jun 2024 13:05:00 +0200 Subject: [PATCH 3/4] Revert "Dispose currentLocalColorTable in final block" This reverts commit 35ea961552dc9a827c6b6119d9ba26b1dd8292fc. --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 487457fbff..d64792eba7 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -463,7 +463,6 @@ private void ReadFrame(BufferedReadStream stream, ref Image? ima finally { indices?.Dispose(); - this.currentLocalColorTable?.Dispose(); } } From 287809c0718e97576886ccfd8013e4165d3d6e03 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 5 Jun 2024 22:44:08 +1000 Subject: [PATCH 4/4] Don't throw; return. --- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 2 +- .../Formats/Gif/GifDecoderTests.cs | 24 +++++-------------- ...2012BadMinCode_Rgba32_issue2012_drona1.png | 3 +++ .../00.png | 3 +++ .../01.png | 3 +++ .../02.png | 3 +++ .../03.png | 3 +++ .../04.png | 3 +++ .../05.png | 3 +++ .../06.png | 3 +++ .../07.png | 3 +++ 11 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 1a05178fdb..aecd8e5b47 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -83,7 +83,7 @@ public void DecodePixels(int minCodeSize, Buffer2D pixels) // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided // color palette but we won't bother since the image is most likely corrupted. - GifThrowHelper.ThrowInvalidImageContentException("Gif Image does not contain a valid LZW minimum code."); + return; } // The resulting index table length. diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 7aabe03481..ee108b9d96 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -296,15 +296,9 @@ public void Issue2012EmptyXmp(TestImageProvider provider) public void Issue2012BadMinCode(TestImageProvider provider) where TPixel : unmanaged, IPixel { - Exception ex = Record.Exception( - () => - { - using Image image = provider.GetImage(); - image.DebugSave(provider); - }); - - Assert.NotNull(ex); - Assert.Contains("Gif Image does not contain a valid LZW minimum code.", ex.Message); + using Image image = provider.GetImage(); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); } // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 @@ -325,14 +319,8 @@ public void IssueDeferredClearCode(TestImageProvider provider) public void IssueTooLargeLzwBits(TestImageProvider provider) where TPixel : unmanaged, IPixel { - Exception ex = Record.Exception( - () => - { - using Image image = provider.GetImage(); - image.DebugSave(provider); - }); - - Assert.NotNull(ex); - Assert.Contains("Gif Image does not contain a valid LZW minimum code.", ex.Message); + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } } diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png new file mode 100644 index 0000000000..cdba9277b1 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a24c066895fd3a76649da376485cbc1912d6a3ae15369575f523e66364b3b6 +size 141563 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png new file mode 100644 index 0000000000..923fbc1225 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:800d1ec2d7c7c99d449db1f49ef202cf18214016eae65ebc4216d6f4b1f4d328 +size 537 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png new file mode 100644 index 0000000000..6c2134d8b8 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94dcd97831b16165f3331e429d72d7ef546e04038cab754c7918f9cf535ff30a +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png new file mode 100644 index 0000000000..6f50397ea4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec1a589a8fae1b17a82b70a9583ea2ee012a476b1fa8fdba27fee2b7ce0403b2 +size 540 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png new file mode 100644 index 0000000000..82061ba0aa --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c8751f4fafd5c56066dbb8d64a3890fc420a3bd66881a55e309ba274b6d14e4 +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png new file mode 100644 index 0000000000..8902eb824a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b78516c9874cb15de4c4b98ed307e8105d962fc6bfa7aa3490b2c7e13b455a2d +size 544 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png new file mode 100644 index 0000000000..82061ba0aa --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c8751f4fafd5c56066dbb8d64a3890fc420a3bd66881a55e309ba274b6d14e4 +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png new file mode 100644 index 0000000000..6f50397ea4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec1a589a8fae1b17a82b70a9583ea2ee012a476b1fa8fdba27fee2b7ce0403b2 +size 540 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png new file mode 100644 index 0000000000..75cf685e43 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:489642f0c81fd12e97007fe6feb11b0e93e351199a922ce038069a3782ad0722 +size 135