From c82b1f7908889195f616e118a732be66600c8d5e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Nov 2025 16:45:01 +1000 Subject: [PATCH 1/2] Explicitly handle missing SOS marker. Fix #2948 --- .../Formats/Jpeg/JpegDecoderCore.cs | 19 ++++++++++++++++++ .../Formats/Jpg/JpegDecoderTests.cs | 20 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Jpg/issues/issue-2948-sos.jpg | 3 +++ 4 files changed, 43 insertions(+) create mode 100644 tests/Images/Input/Jpg/issues/issue-2948-sos.jpg diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 0ad78b9035..7825955e7a 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -71,6 +71,11 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData /// private bool hasAdobeMarker; + /// + /// Whether the image has a SOS marker. + /// + private bool hasSOSMarker; + /// /// Contains information about the JFIF marker. /// @@ -197,6 +202,12 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData { using SpectralConverter spectralConverter = new(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize); this.ParseStream(stream, spectralConverter, cancellationToken); + + if (!this.hasSOSMarker) + { + JpegThrowHelper.ThrowInvalidImageContentException("Missing SOS marker."); + } + this.InitExifProfile(); this.InitIccProfile(); this.InitIptcProfile(); @@ -215,6 +226,12 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ParseStream(stream, spectralConverter: null, cancellationToken); + + if (!this.hasSOSMarker) + { + JpegThrowHelper.ThrowInvalidImageContentException("Missing SOS marker."); + } + this.InitExifProfile(); this.InitIccProfile(); this.InitIptcProfile(); @@ -403,6 +420,8 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData break; case JpegConstants.Markers.SOS: + + this.hasSOSMarker = true; if (!metadataOnly) { this.ProcessStartOfScanMarker(stream, markerContentByteSize); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 71753bf9ca..6dd26cdcb4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -417,4 +417,24 @@ public partial class JpegDecoderTests image.DebugSave(provider); image.CompareToReferenceOutput(provider); } + + // https://github.com/SixLabors/ImageSharp/issues/2948 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2948, PixelTypes.Rgb24)] + public void Issue2948_No_SOS_Decode_Throws_InvalidImageContentException(TestImageProvider provider) + where TPixel : unmanaged, IPixel + => Assert.Throws(() => + { + using Image image = provider.GetImage(JpegDecoder.Instance); + }); + + // https://github.com/SixLabors/ImageSharp/issues/2948 + [Theory] + [InlineData(TestImages.Jpeg.Issues.Issue2948)] + public void Issue2948_No_SOS_Identify_Throws_InvalidImageContentException(string imagePath) + => Assert.Throws(() => + { + TestFile testFile = TestFile.Create(imagePath); + ImageInfo imageInfo = Image.Identify(testFile.Bytes); + }); } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 94dcd2b7b9..bc699da88e 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -348,6 +348,7 @@ public static class TestImages public const string Issue2638 = "Jpg/issues/Issue2638.jpg"; public const string Issue2758 = "Jpg/issues/issue-2758.jpg"; public const string Issue2857 = "Jpg/issues/issue-2857-subsub-ifds.jpg"; + public const string Issue2948 = "Jpg/issues/issue-2948-sos.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg b/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg new file mode 100644 index 0000000000..d210e87e52 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad9c3a6babb41c5aa2a46fbfe74ed5482028c73f48531cf144e1e324ca7988b3 +size 103789 From 143c3bb4f2f9a66e130a40f998dd757bc51d18b9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Nov 2025 17:15:53 +1000 Subject: [PATCH 2/2] Cleanup --- .github/workflows/build-and-test.yml | 6 ------ tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs | 6 +----- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3e3d6d0e27..41136bfd8a 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -62,8 +62,6 @@ jobs: needs: WarmLFS strategy: matrix: - isARM: - - ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }} options: - os: ubuntu-latest framework: net9.0 @@ -121,10 +119,6 @@ jobs: sdk: 8.0.x runtime: -x64 codecov: false - exclude: - - isARM: false - options: - os: buildjet-4vcpu-ubuntu-2204-arm runs-on: ${{ matrix.options.os }} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 6dd26cdcb4..3fd55eb915 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -432,9 +432,5 @@ public partial class JpegDecoderTests [Theory] [InlineData(TestImages.Jpeg.Issues.Issue2948)] public void Issue2948_No_SOS_Identify_Throws_InvalidImageContentException(string imagePath) - => Assert.Throws(() => - { - TestFile testFile = TestFile.Create(imagePath); - ImageInfo imageInfo = Image.Identify(testFile.Bytes); - }); + => Assert.Throws(() => _ = Image.Identify(TestFile.Create(imagePath).Bytes)); }