Browse Source

Jpeg Fuzz Fixes (#836)

* Nomalize jpeg exceptions. Fix #821

* Fix #822

* Fix #823

* Check for correct QT index. Touch #824

* Check DHT props. Touch #824

* Limit sampling factors to 1 & 2. Touch #824

* Add already fixed image 4. Touch #824

* Check for excessive code lengths. Touch #824

* Add already fixed image 6. Touch #824

* Lint progressive scan details. Touch #824

* Add already fixed image 8. Fix #824

* Remove duplicate per-block checks

* Add already fixed image 1. Touch #825

* Don't throw on bad JFIF density units.

* Add already fixed image 3. Touch #825

* Add already fixed image 4. Fix #825

* Check SOFn marker length. Touch #826

* Add already fixed image 2. Touch #826

* Add already fixed image 3. Fix #826

* Add fixed already fixed image. Fix #827

* Revert unneeded bounds check introduced in #804
af/merge-core
James Jackson-South 7 years ago
committed by GitHub
parent
commit
1054f84253
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  2. 19
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  3. 21
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
  4. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
  5. 62
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
  6. 92
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  7. 35
      src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
  8. 19
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
  9. 28
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
  10. 29
      tests/ImageSharp.Tests/TestImages.cs
  11. 2
      tests/Images/External
  12. 0
      tests/Images/Input/Jpg/issues/Issue214-CriticalEOF.jpg
  13. 0
      tests/Images/Input/Jpg/issues/fuzz/Issue797-NullReferenceException.jpg
  14. 0
      tests/Images/Input/Jpg/issues/fuzz/Issue798-AccessViolationException.jpg
  15. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue821-DivideByZeroException.jpg
  16. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue822-DivideByZeroException.jpg
  17. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue823-NullReferenceException.jpg
  18. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-A.jpg
  19. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-B.jpg
  20. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-C.jpg
  21. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-D.jpg
  22. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-E.jpg
  23. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-F.jpg
  24. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-G.jpg
  25. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue824-IndexOutOfRangeException-H.jpg
  26. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-A.jpg
  27. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-B.jpg
  28. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-C.jpg
  29. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue825-ArgumentOutOfRangeException-D.jpg
  30. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-A.jpg
  31. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-B.jpg
  32. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue826-ArgumentException-C.jpg
  33. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue827-AccessViolationException.jpg

22
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs

@ -50,10 +50,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="values">The huffman values</param>
public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> 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<short> huffcode = memoryAllocator.Allocate<short>(HuffCodeLength))
const int Length = 257;
using (IMemoryOwner<short> huffcode = memoryAllocator.Allocate<short>(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;
}
}
}

19
src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs

@ -27,12 +27,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="yDensity">The vertical pixel density</param>
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
}
/// <inheritdoc/>
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);
/// <inheritdoc/>
public override int GetHashCode()

21
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;

2
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;

62
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<Block8x8, short>(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<Block8x8, short>(ref block);
if (this.successiveHigh == 0)

92
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;
}
/// <summary>
@ -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
/// <param name="remaining">The remaining bytes in the segment block.</param>
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<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
if (this.ImageWidth == 0 || this.ImageHeight == 0)
{
JpegThrowHelper.ThrowInvalidImageDimensions(this.ImageWidth, this.ImageHeight);
}
var image = Image.CreateUninitialized<TPixel>(
this.configuration,
this.ImageWidth,

35
src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs

@ -8,19 +8,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
internal static class JpegThrowHelper
{
/// <summary>
/// Cold path optimization for throwing <see cref="ImageFormatException"/>-s
/// Cold path optimization for throwing <see cref="ImageFormatException"/>'s.
/// </summary>
/// <param name="errorMessage">The error message for the exception</param>
[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.");
}
/// <param name="errorMessage">The error message for the exception.</param>
[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}.");
}
}

19
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
// TODO: We need a public ImageDecoderException class in ImageSharp!
Assert.ThrowsAny<Exception>(() => provider.GetImage(JpegDecoder));
}
[Theory]
[WithFile(TestImages.Jpeg.Issues.InvalidJpegThrowsWrongException797, PixelTypes.Rgba32)]
public void LoadingImage_InvalidTagLength_ShouldThrow<TPixel>(TestImageProvider<TPixel> provider)
[WithFileCollection(nameof(UnrecoverableTestJpegs), PixelTypes.Rgba32)]
public void UnrecoverableImagesShouldThrowCorrectError<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel> => Assert.Throws<ImageFormatException>(() => provider.GetImage());
[Theory]
[WithFile(TestImages.Jpeg.Issues.AccessViolationException798, PixelTypes.Rgba32)]
public void LoadingImage_BadHuffman_ShouldNotThrow<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel> => Assert.NotNull(provider.GetImage());
}
}

28
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<string, float> CustomToleranceValues =
new Dictionary<string, float>
{

29
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,

2
tests/Images/External

@ -1 +1 @@
Subproject commit 32dc8aec1109b681f056264e6bde1f3a76f08aba
Subproject commit c4098e463ab0e7128ae196b7f963369271df8fd3

0
tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg → tests/Images/Input/Jpg/issues/Issue214-CriticalEOF.jpg

0
tests/Images/Input/Jpg/issues/Issue797-InvalidImage.jpg → tests/Images/Input/Jpg/issues/fuzz/Issue797-NullReferenceException.jpg

0
tests/Images/Input/Jpg/issues/Issue798-AccessViolationException.jpg → tests/Images/Input/Jpg/issues/fuzz/Issue798-AccessViolationException.jpg

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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

3
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
Loading…
Cancel
Save