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