diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
index 9e11981b12..90b3624651 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
@@ -50,10 +50,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// The huffman values
public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values)
{
- // We do some bounds checks in the code here to protect against AccessViolationExceptions
- const int HuffCodeLength = 257;
- const int MaxSizeLength = HuffCodeLength - 1;
- using (IMemoryOwner huffcode = memoryAllocator.Allocate(HuffCodeLength))
+ const int Length = 257;
+ using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length))
{
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan());
ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths);
@@ -65,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (short i = 1; i < 17; i++)
{
byte length = Unsafe.Add(ref codeLengthsRef, i);
- for (short j = 0; j < length && x < MaxSizeLength; j++)
+ for (short j = 0; j < length; j++)
{
Unsafe.Add(ref sizesRef, x++) = i;
}
@@ -86,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Unsafe.Add(ref valOffsetRef, k) = (int)(si - code);
if (Unsafe.Add(ref sizesRef, si) == k)
{
- while (Unsafe.Add(ref sizesRef, si) == k && si < HuffCodeLength)
+ while (Unsafe.Add(ref sizesRef, si) == k)
{
Unsafe.Add(ref huffcodeRef, si++) = (short)code++;
}
@@ -103,19 +101,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Generate non-spec lookup tables to speed up decoding.
const int FastBits = ScanDecoder.FastBits;
ref byte lookaheadRef = ref this.Lookahead[0];
- const uint MaxFastLength = 1 << FastBits;
- Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, MaxFastLength); // Flag for non-accelerated
+ Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, 1 << FastBits); // Flag for non-accelerated
for (int i = 0; i < si; i++)
{
int size = Unsafe.Add(ref sizesRef, i);
if (size <= FastBits)
{
- int huffCode = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size);
- int max = 1 << (FastBits - size);
- for (int left = 0; left < max; left++)
+ int fastOffset = FastBits - size;
+ int fastCode = Unsafe.Add(ref huffcodeRef, i) << fastOffset;
+ int fastMax = 1 << fastOffset;
+ for (int left = 0; left < fastMax; left++)
{
- Unsafe.Add(ref lookaheadRef, huffCode + left) = (byte)i;
+ Unsafe.Add(ref lookaheadRef, fastCode + left) = (byte)i;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
index c51a2f4da5..76f0e2cb51 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
@@ -27,12 +27,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// The vertical pixel density
private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity)
{
- Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity));
- Guard.MustBeGreaterThan(yDensity, 0, nameof(yDensity));
- Guard.MustBeBetweenOrEqualTo(densityUnits, 0, 2, nameof(densityUnits));
+ if (xDensity <= 0)
+ {
+ JpegThrowHelper.ThrowImageFormatException($"X-Density {xDensity} must be greater than 0.");
+ }
+
+ if (yDensity <= 0)
+ {
+ JpegThrowHelper.ThrowImageFormatException($"Y-Density {yDensity} must be greater than 0.");
+ }
this.MajorVersion = majorVersion;
this.MinorVersion = minorVersion;
+
+ // LibJpeg and co will simply cast and not try to enforce a range.
this.DensityUnits = (PixelResolutionUnit)densityUnits;
this.XDensity = xDensity;
this.YDensity = yDensity;
@@ -104,10 +112,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
///
- public override bool Equals(object obj)
- {
- return obj is JFifMarker other && this.Equals(other);
- }
+ public override bool Equals(object obj) => obj is JFifMarker other && this.Equals(other);
///
public override int GetHashCode()
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
index cfed5d535f..ac24f4ca58 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
@@ -21,9 +21,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.memoryAllocator = memoryAllocator;
this.Frame = frame;
this.Id = id;
+
+ // Valid sampling factors are 1..2
+ if (horizontalFactor == 0
+ || verticalFactor == 0
+ || horizontalFactor > 2
+ || verticalFactor > 2)
+ {
+ JpegThrowHelper.ThrowBadSampling();
+ }
+
this.HorizontalSamplingFactor = horizontalFactor;
this.VerticalSamplingFactor = verticalFactor;
this.SamplingFactors = new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor);
+
+ if (quantizationTableIndex > 3)
+ {
+ JpegThrowHelper.ThrowBadQuantizationTable();
+ }
+
this.QuantizationTableIndex = quantizationTableIndex;
this.Index = index;
}
@@ -110,6 +126,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegComponent c0 = this.Frame.Components[0];
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
+ if (this.SubSamplingDivisors.Width == 0 || this.SubSamplingDivisors.Height == 0)
+ {
+ JpegThrowHelper.ThrowBadSampling();
+ }
+
int totalNumberOfBlocks = blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
int width = this.WidthInBlocks + 1;
int height = totalNumberOfBlocks / width;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
index 36a3dc2d26..33a7c68baf 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
@@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
for (int i = 0; i < this.Components.Length; i++)
{
- this.Components[i].Dispose();
+ this.Components[i]?.Dispose();
}
this.Components = null;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
index a1c1b023e4..070652a9f9 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
@@ -270,8 +270,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
+ private void CheckProgressiveData()
+ {
+ // Validate successive scan parameters.
+ // Logic has been adapted from libjpeg.
+ // See Table B.3 – Scan header parameter size and values. itu-t81.pdf
+ bool invalid = false;
+ if (this.spectralStart == 0)
+ {
+ if (this.spectralEnd != 0)
+ {
+ invalid = true;
+ }
+ }
+ else
+ {
+ // Need not check Ss/Se < 0 since they came from unsigned bytes.
+ if (this.spectralEnd < this.spectralStart || this.spectralEnd > 63)
+ {
+ invalid = true;
+ }
+
+ // AC scans may have only one component.
+ if (this.componentsLength != 1)
+ {
+ invalid = true;
+ }
+ }
+
+ if (this.successiveHigh != 0)
+ {
+ // Successive approximation refinement scan: must have Al = Ah-1.
+ if (this.successiveHigh - 1 != this.successiveLow)
+ {
+ invalid = true;
+ }
+ }
+
+ // TODO: How does this affect 12bit jpegs.
+ // According to libjpeg the range covers 8bit only?
+ if (this.successiveLow > 13)
+ {
+ invalid = true;
+ }
+
+ if (invalid)
+ {
+ JpegThrowHelper.ThrowBadProgressiveScan(this.spectralStart, this.spectralEnd, this.successiveHigh, this.successiveLow);
+ }
+ }
+
private void ParseProgressiveData()
{
+ this.CheckProgressiveData();
+
if (this.componentsLength == 1)
{
this.ParseProgressiveDataNonInterleaved();
@@ -483,11 +535,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref Block8x8 block,
ref HuffmanTable dcTable)
{
- if (this.spectralEnd != 0)
- {
- JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
- }
-
this.CheckBits();
ref short blockDataRef = ref Unsafe.As(ref block);
@@ -518,11 +565,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanTable acTable,
ref short fastACRef)
{
- if (this.spectralStart == 0)
- {
- JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
- }
-
ref short blockDataRef = ref Unsafe.As(ref block);
if (this.successiveHigh == 0)
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 06c844d58e..9bcfd9ff64 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -255,7 +255,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
if (fileMarker.Marker != JpegConstants.Markers.SOI)
{
- throw new ImageFormatException("Missing SOI marker.");
+ JpegThrowHelper.ThrowImageFormatException("Missing SOI marker.");
}
this.InputStream.Read(this.markerBuffer, 0, 2);
@@ -419,7 +419,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
: JpegColorSpace.Cmyk;
}
- throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}");
+ JpegThrowHelper.ThrowImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}");
+ return default;
}
///
@@ -646,6 +647,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
bool done = false;
remaining--;
int quantizationTableSpec = this.InputStream.ReadByte();
+ int tableIndex = quantizationTableSpec & 15;
+
+ // Max index. 4 Tables max.
+ if (tableIndex > 3)
+ {
+ JpegThrowHelper.ThrowBadQuantizationTable();
+ }
switch (quantizationTableSpec >> 4)
{
@@ -661,7 +669,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(this.temp, 0, 64);
remaining -= 64;
- ref Block8x8F table = ref this.QuantizationTables[quantizationTableSpec & 15];
+ ref Block8x8F table = ref this.QuantizationTables[tableIndex];
for (int j = 0; j < 64; j++)
{
table[j] = this.temp[j];
@@ -681,7 +689,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(this.temp, 0, 128);
remaining -= 128;
- ref Block8x8F table = ref this.QuantizationTables[quantizationTableSpec & 15];
+ ref Block8x8F table = ref this.QuantizationTables[tableIndex];
for (int j = 0; j < 64; j++)
{
table[j] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1];
@@ -690,7 +698,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
break;
default:
- throw new ImageFormatException("Bad Tq index value");
+ JpegThrowHelper.ThrowBadQuantizationTable();
+ break;
}
if (done)
@@ -701,7 +710,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (remaining != 0)
{
- throw new ImageFormatException("DQT has wrong length");
+ JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DQT), remaining);
}
this.MetaData.GetFormatMetaData(JpegFormat.Instance).Quality = QualityEvaluator.EstimateQuality(this.QuantizationTables);
@@ -717,15 +726,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (this.Frame != null)
{
- throw new ImageFormatException("Multiple SOF markers. Only single frame jpegs supported.");
+ JpegThrowHelper.ThrowImageFormatException("Multiple SOF markers. Only single frame jpegs supported.");
}
- this.InputStream.Read(this.temp, 0, remaining);
+ // Read initial marker definitions.
+ const int length = 6;
+ this.InputStream.Read(this.temp, 0, length);
// We only support 8-bit and 12-bit precision.
if (!this.supportedPrecisions.Contains(this.temp[0]))
{
- throw new ImageFormatException("Only 8-Bit and 12-Bit precision supported.");
+ JpegThrowHelper.ThrowImageFormatException("Only 8-Bit and 12-Bit precision supported.");
}
this.Precision = this.temp[0];
@@ -740,22 +751,35 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ComponentCount = this.temp[5]
};
- this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines);
-
- int maxH = 0;
- int maxV = 0;
- int index = 6;
+ if (this.Frame.SamplesPerLine == 0 || this.Frame.Scanlines == 0)
+ {
+ JpegThrowHelper.ThrowInvalidImageDimensions(this.Frame.SamplesPerLine, this.Frame.Scanlines);
+ }
+ this.ImageSizeInPixels = new Size(this.Frame.SamplesPerLine, this.Frame.Scanlines);
this.ComponentCount = this.Frame.ComponentCount;
if (!metadataOnly)
{
+ remaining -= length;
+
+ const int componentBytes = 3;
+ if (remaining > this.ComponentCount * componentBytes)
+ {
+ JpegThrowHelper.ThrowBadMarker("SOFn", remaining);
+ }
+
+ this.InputStream.Read(this.temp, 0, remaining);
+
// No need to pool this. They max out at 4
this.Frame.ComponentIds = new byte[this.ComponentCount];
this.Frame.ComponentOrder = new byte[this.ComponentCount];
this.Frame.Components = new JpegComponent[this.ComponentCount];
this.ColorSpace = this.DeduceJpegColorSpace();
+ int maxH = 0;
+ int maxV = 0;
+ int index = 0;
for (int i = 0; i < this.ComponentCount; i++)
{
byte hv = this.temp[index + 1];
@@ -777,7 +801,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id;
- index += 3;
+ index += componentBytes;
}
this.Frame.MaxHorizontalFactor = maxH;
@@ -795,12 +819,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// The remaining bytes in the segment block.
private void ProcessDefineHuffmanTablesMarker(int remaining)
{
+ int length = remaining;
+
using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
{
ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan());
for (int i = 2; i < remaining;)
{
byte huffmanTableSpec = (byte)this.InputStream.ReadByte();
+ int tableType = huffmanTableSpec >> 4;
+ int tableIndex = huffmanTableSpec & 15;
+
+ // Types 0..1 DC..AC
+ if (tableType > 1)
+ {
+ JpegThrowHelper.ThrowImageFormatException("Bad Huffman Table type.");
+ }
+
+ // Max tables of each type
+ if (tableIndex > 3)
+ {
+ JpegThrowHelper.ThrowImageFormatException("Bad Huffman Table index.");
+ }
+
this.InputStream.Read(huffmanData.Array, 0, 16);
using (IManagedByteBuffer codeLengths = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean))
@@ -813,15 +854,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
codeLengthSum += Unsafe.Add(ref codeLengthsRef, j) = Unsafe.Add(ref huffmanDataRef, j - 1);
}
+ length -= 17;
+
+ if (codeLengthSum > 256 || codeLengthSum > length)
+ {
+ JpegThrowHelper.ThrowImageFormatException("Huffman table has excessive length.");
+ }
+
using (IManagedByteBuffer huffmanValues = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean))
{
this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum);
i += 17 + codeLengthSum;
- int tableType = huffmanTableSpec >> 4;
- int tableIndex = huffmanTableSpec & 15;
-
this.BuildHuffmanTable(
tableType == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
tableIndex,
@@ -848,7 +893,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (remaining != 2)
{
- throw new ImageFormatException($"DRI has wrong length: {remaining}");
+ JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.DRI), remaining);
}
this.resetInterval = this.ReadUint16();
@@ -861,7 +906,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (this.Frame is null)
{
- throw new ImageFormatException("No readable SOFn (Start Of Frame) marker found.");
+ JpegThrowHelper.ThrowImageFormatException("No readable SOFn (Start Of Frame) marker found.");
}
int selectorsCount = this.InputStream.ReadByte();
@@ -882,7 +927,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (componentIndex < 0)
{
- throw new ImageFormatException("Unknown component selector");
+ JpegThrowHelper.ThrowImageFormatException($"Unknown component selector {componentIndex}.");
}
ref JpegComponent component = ref this.Frame.Components[componentIndex];
@@ -944,6 +989,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private Image PostProcessIntoImage()
where TPixel : struct, IPixel
{
+ if (this.ImageWidth == 0 || this.ImageHeight == 0)
+ {
+ JpegThrowHelper.ThrowInvalidImageDimensions(this.ImageWidth, this.ImageHeight);
+ }
+
var image = Image.CreateUninitialized(
this.configuration,
this.ImageWidth,
diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
index c7f3666604..b30da28c45 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
@@ -8,19 +8,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
internal static class JpegThrowHelper
{
///
- /// Cold path optimization for throwing -s
+ /// Cold path optimization for throwing 's.
///
- /// The error message for the exception
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowImageFormatException(string errorMessage)
- {
- throw new ImageFormatException(errorMessage);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowBadHuffmanCode()
- {
- throw new ImageFormatException("Bad Huffman code.");
- }
+ /// The error message for the exception.
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowImageFormatException(string errorMessage) => throw new ImageFormatException(errorMessage);
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadMarker(string marker, int length) => throw new ImageFormatException($"Marker {marker} has bad length {length}.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadQuantizationTable() => throw new ImageFormatException("Bad Quantization Table index.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadHuffmanCode() => throw new ImageFormatException("Bad Huffman code.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadSampling() => throw new ImageFormatException("Bad sampling factor.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowBadProgressiveScan(int ss, int se, int ah, int al) => throw new ImageFormatException($"Invalid progressive parameters Ss={ss} Se={se} Ah={ah} Al={al}.");
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowInvalidImageDimensions(int width, int height) => throw new ImageFormatException($"Invalid image dimensions: {width}x{height}.");
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
index 1c9d207cd1..31a5a0eeb0 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
@@ -35,22 +34,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
[Theory]
- [WithFile(TestImages.Jpeg.Issues.CriticalEOF214, PixelTypes.Rgba32)]
- public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow(TestImageProvider provider)
- where TPixel : struct, IPixel
- {
- // TODO: We need a public ImageDecoderException class in ImageSharp!
- Assert.ThrowsAny(() => provider.GetImage(JpegDecoder));
- }
-
- [Theory]
- [WithFile(TestImages.Jpeg.Issues.InvalidJpegThrowsWrongException797, PixelTypes.Rgba32)]
- public void LoadingImage_InvalidTagLength_ShouldThrow(TestImageProvider provider)
+ [WithFileCollection(nameof(UnrecoverableTestJpegs), PixelTypes.Rgba32)]
+ public void UnrecoverableImagesShouldThrowCorrectError(TestImageProvider provider)
where TPixel : struct, IPixel => Assert.Throws(() => provider.GetImage());
-
- [Theory]
- [WithFile(TestImages.Jpeg.Issues.AccessViolationException798, PixelTypes.Rgba32)]
- public void LoadingImage_BadHuffman_ShouldNotThrow(TestImageProvider provider)
- where TPixel : struct, IPixel => Assert.NotNull(provider.GetImage());
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
index 9f34d7d09b..ff49de2481 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
@@ -31,6 +31,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Issues.ExifGetString750Load,
TestImages.Jpeg.Issues.ExifGetString750Transform,
+ // LibJpeg can open this despite the invalid desity units.
+ TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B,
+
// High depth images
TestImages.Jpeg.Baseline.Testorig12bit,
};
@@ -54,6 +57,31 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C
};
+ public static string[] UnrecoverableTestJpegs = {
+
+ TestImages.Jpeg.Issues.CriticalEOF214,
+ TestImages.Jpeg.Issues.Fuzz.NullReferenceException797,
+ 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.IndexOutOfRangeException824C,
+ TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824D,
+ TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824E,
+ TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824F,
+ TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824G,
+ TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824H,
+ TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825A,
+ TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825C,
+ TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825D,
+ TestImages.Jpeg.Issues.Fuzz.ArgumentException826A,
+ TestImages.Jpeg.Issues.Fuzz.ArgumentException826B,
+ TestImages.Jpeg.Issues.Fuzz.ArgumentException826C,
+ TestImages.Jpeg.Issues.Fuzz.AccessViolationException827
+ };
+
private static readonly Dictionary CustomToleranceValues =
new Dictionary
{
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index e28caf6277..c99fe8d652 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -172,8 +172,31 @@ namespace SixLabors.ImageSharp.Tests
public const string OrderedInterleavedProgressive723C = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg";
public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg";
public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg";
- public const string InvalidJpegThrowsWrongException797 = "Jpg/issues/Issue797-InvalidImage.jpg";
- public const string AccessViolationException798 = "Jpg/issues/Issue798-AccessViolationException.jpg";
+
+ public static class Fuzz
+ {
+ public const string NullReferenceException797 = "Jpg/issues/fuzz/Issue797-NullReferenceException.jpg";
+ public const string AccessViolationException798 = "Jpg/issues/fuzz/Issue798-AccessViolationException.jpg";
+ public const string DivideByZeroException821 = "Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg";
+ public const string DivideByZeroException822 = "Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg";
+ public const string NullReferenceException823 = "Jpg/issues/fuzz/Issue823-NullReferenceException.jpg";
+ public const string IndexOutOfRangeException824A = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg";
+ public const string IndexOutOfRangeException824B = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg";
+ public const string IndexOutOfRangeException824C = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg";
+ public const string IndexOutOfRangeException824D = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg";
+ public const string IndexOutOfRangeException824E = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg";
+ public const string IndexOutOfRangeException824F = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg";
+ public const string IndexOutOfRangeException824G = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg";
+ public const string IndexOutOfRangeException824H = "Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg";
+ public const string ArgumentOutOfRangeException825A = "Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg";
+ public const string ArgumentOutOfRangeException825B = "Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg";
+ public const string ArgumentOutOfRangeException825C = "Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg";
+ public const string ArgumentOutOfRangeException825D = "Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg";
+ public const string ArgumentException826A = "Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg";
+ public const string ArgumentException826B = "Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg";
+ public const string ArgumentException826C = "Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg";
+ public const string AccessViolationException827 = "Jpg/issues/fuzz/Issue827-AccessViolationException.jpg";
+ }
}
public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray();
@@ -236,7 +259,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Rgba321010102 = "Bmp/rgba32-1010102.bmp";
public const string RgbaAlphaBitfields = "Bmp/rgba32abf.bmp";
- public static readonly string[] BitFields
+ public static readonly string[] BitFields
= {
Rgb32bfdef,
Rgb32bf,
diff --git a/tests/Images/External b/tests/Images/External
index 32dc8aec11..c4098e463a 160000
--- a/tests/Images/External
+++ b/tests/Images/External
@@ -1 +1 @@
-Subproject commit 32dc8aec1109b681f056264e6bde1f3a76f08aba
+Subproject commit c4098e463ab0e7128ae196b7f963369271df8fd3
diff --git a/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg b/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF.jpg
similarity index 100%
rename from tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg
rename to tests/Images/Input/Jpg/issues/Issue214-CriticalEOF.jpg
diff --git a/tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue797-NullReferenceException.jpg
similarity index 100%
rename from tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg
rename to tests/Images/Input/Jpg/issues/fuzz/Issue797-NullReferenceException.jpg
diff --git a/tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue798-AccessViolationException.jpg
similarity index 100%
rename from tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg
rename to tests/Images/Input/Jpg/issues/fuzz/Issue798-AccessViolationException.jpg
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg
new file mode 100644
index 0000000000..23e95f1238
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:44f42f4b5c0491b7e8f5fe4b6a580c99bc5246cfe95238c601c75a7c5926e4b1
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg
new file mode 100644
index 0000000000..8a6fd02913
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:15107f755765118c8a1afac4a58f2945b3bd503ddabcc5b831219d1a159209d9
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue823-NullReferenceException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue823-NullReferenceException.jpg
new file mode 100644
index 0000000000..a404664aeb
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue823-NullReferenceException.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0aa96e68b91b56c4a6536e421e9a87a8388d1f205c3e1c3f7fd8f954a547fb26
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg
new file mode 100644
index 0000000000..7df0579b03
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:19f76ba0a41511f384118214395f6f0911909b2e2304b7b2caf464e512c59006
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg
new file mode 100644
index 0000000000..396cddb599
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9b75a87a3beb879c04bfac9ff5b2f11d939ac579d4d5e04fcc912ac95d38101e
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg
new file mode 100644
index 0000000000..4bd164ddd6
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0ee6b88dad441210f443cad24c6edba460fcc26404fcbbf0c134f9ccb3e92b0d
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg
new file mode 100644
index 0000000000..c80d7ba050
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3d17827181a497a5e0ca78d41347385f329eaa7fe25e8adb7cd7842ae1a656c8
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg
new file mode 100644
index 0000000000..8d01b7427e
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:617a0b15d8ddd07cd8c1d9a2e9fb1d09fa1b4756db7b91c5fbe1de233787ea1a
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg
new file mode 100644
index 0000000000..6430317995
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4834f4b8274e32ed44872a5365dd3cedd219b58538af592abf1692698afb3a95
+size 411
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg
new file mode 100644
index 0000000000..cc686eee0d
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b0d3d7230934adb7f44875cfba7ce1688a10093e322712dc7fb2f2f07dfb93c6
+size 308
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg
new file mode 100644
index 0000000000..132c9043f7
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1478c189574a9c5f9492ae1f9ea2093a74255299d0f293d7fe9c41a76be381e3
+size 308
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg
new file mode 100644
index 0000000000..845a526350
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c095034c82addf710d5d9127447b894bb052642800c40b803517547c0b316054
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg
new file mode 100644
index 0000000000..57dd33e234
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b12df8907f0a9a0a89ac24ff73db43d8e85d47558a20cb186764cc0533bdb214
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg
new file mode 100644
index 0000000000..2e595e36ae
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:78ca8307aaacd980939be0002210dacbe09e133a8249f0d671e89f45b229abc0
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg
new file mode 100644
index 0000000000..7c3db77d85
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c6dba5ec29565cbc9a70b0ea70fe03409d73cfef6af9a8519b579bc4947252bf
+size 380
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg
new file mode 100644
index 0000000000..5766e13d53
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dfd61ccf7df77b9eff15bca98fad4df0eef095c8c8ca690344fc459d9706a50c
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg
new file mode 100644
index 0000000000..625ecc61d4
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60a198357053526cbfc0fd9fc90d8690cdc72b9b75e88a926d6e7b7664a89343
+size 397
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg
new file mode 100644
index 0000000000..fa11080385
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:38c6415405ac1303d359a0327a3b2517e7bad99df9fd3c0f4cbae979be56f0d7
+size 409
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue827-AccessViolationException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue827-AccessViolationException.jpg
new file mode 100644
index 0000000000..177bb082a9
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue827-AccessViolationException.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e7282c0dbea030f2e18eef12685dc17fa37b03e0f67cf71d8917106d470b94f
+size 397