Browse Source

Merge pull request #2084 from br3aker/dp/jpeg-marker-validation

Added sanity check for every jpeg marker
pull/2092/head
Anton Firszov 4 years ago
committed by GitHub
parent
commit
5d0c6840d6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 40
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  2. 3
      src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
  3. 9
      src/ImageSharp/IO/BufferedReadStream.cs
  4. 1
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
  5. 1
      tests/ImageSharp.Tests/TestImages.cs
  6. 3
      tests/Images/Input/Jpg/issues/fuzz/Issue2085-NullReferenceException.jpg

40
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -315,14 +315,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (!fileMarker.Invalid) if (!fileMarker.Invalid)
{ {
// Get the marker length. // Get the marker length.
int remaining = this.ReadUint16(stream) - 2; int markerContentByteSize = this.ReadUint16(stream) - 2;
// Check whether stream actually has enought bytes to read
// markerContentByteSize is always positive so we cast
// to uint to avoid sign extension
if (stream.RemainingBytes < (uint)markerContentByteSize)
{
JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
}
switch (fileMarker.Marker) switch (fileMarker.Marker)
{ {
case JpegConstants.Markers.SOF0: case JpegConstants.Markers.SOF0:
case JpegConstants.Markers.SOF1: case JpegConstants.Markers.SOF1:
case JpegConstants.Markers.SOF2: case JpegConstants.Markers.SOF2:
this.ProcessStartOfFrameMarker(stream, remaining, fileMarker, metadataOnly); this.ProcessStartOfFrameMarker(stream, markerContentByteSize, fileMarker, metadataOnly);
break; break;
case JpegConstants.Markers.SOF5: case JpegConstants.Markers.SOF5:
@ -350,7 +358,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegConstants.Markers.SOS: case JpegConstants.Markers.SOS:
if (!metadataOnly) if (!metadataOnly)
{ {
this.ProcessStartOfScanMarker(stream, remaining); this.ProcessStartOfScanMarker(stream, markerContentByteSize);
break; break;
} }
else else
@ -364,41 +372,41 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (metadataOnly) if (metadataOnly)
{ {
stream.Skip(remaining); stream.Skip(markerContentByteSize);
} }
else else
{ {
this.ProcessDefineHuffmanTablesMarker(stream, remaining); this.ProcessDefineHuffmanTablesMarker(stream, markerContentByteSize);
} }
break; break;
case JpegConstants.Markers.DQT: case JpegConstants.Markers.DQT:
this.ProcessDefineQuantizationTablesMarker(stream, remaining); this.ProcessDefineQuantizationTablesMarker(stream, markerContentByteSize);
break; break;
case JpegConstants.Markers.DRI: case JpegConstants.Markers.DRI:
if (metadataOnly) if (metadataOnly)
{ {
stream.Skip(remaining); stream.Skip(markerContentByteSize);
} }
else else
{ {
this.ProcessDefineRestartIntervalMarker(stream, remaining); this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize);
} }
break; break;
case JpegConstants.Markers.APP0: case JpegConstants.Markers.APP0:
this.ProcessApplicationHeaderMarker(stream, remaining); this.ProcessApplicationHeaderMarker(stream, markerContentByteSize);
break; break;
case JpegConstants.Markers.APP1: case JpegConstants.Markers.APP1:
this.ProcessApp1Marker(stream, remaining); this.ProcessApp1Marker(stream, markerContentByteSize);
break; break;
case JpegConstants.Markers.APP2: case JpegConstants.Markers.APP2:
this.ProcessApp2Marker(stream, remaining); this.ProcessApp2Marker(stream, markerContentByteSize);
break; break;
case JpegConstants.Markers.APP3: case JpegConstants.Markers.APP3:
@ -411,20 +419,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegConstants.Markers.APP10: case JpegConstants.Markers.APP10:
case JpegConstants.Markers.APP11: case JpegConstants.Markers.APP11:
case JpegConstants.Markers.APP12: case JpegConstants.Markers.APP12:
stream.Skip(remaining); stream.Skip(markerContentByteSize);
break; break;
case JpegConstants.Markers.APP13: case JpegConstants.Markers.APP13:
this.ProcessApp13Marker(stream, remaining); this.ProcessApp13Marker(stream, markerContentByteSize);
break; break;
case JpegConstants.Markers.APP14: case JpegConstants.Markers.APP14:
this.ProcessApp14Marker(stream, remaining); this.ProcessApp14Marker(stream, markerContentByteSize);
break; break;
case JpegConstants.Markers.APP15: case JpegConstants.Markers.APP15:
case JpegConstants.Markers.COM: case JpegConstants.Markers.COM:
stream.Skip(remaining); stream.Skip(markerContentByteSize);
break; break;
case JpegConstants.Markers.DAC: case JpegConstants.Markers.DAC:
@ -1260,7 +1268,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int selectorsBytes = selectorsCount * 2; int selectorsBytes = selectorsCount * 2;
if (remaining != 4 + selectorsBytes) if (remaining != 4 + selectorsBytes)
{ {
JpegThrowHelper.ThrowBadMarker("SOS", remaining); JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.SOS), remaining);
} }
// selectorsCount*2 bytes: component index + huffman tables indices // selectorsCount*2 bytes: component index + huffman tables indices

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

@ -25,6 +25,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowBadMarker(string marker, int length) => throw new InvalidImageContentException($"Marker {marker} has bad length {length}."); public static void ThrowBadMarker(string marker, int length) => throw new InvalidImageContentException($"Marker {marker} has bad length {length}.");
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNotEnoughBytesForMarker(byte marker) => throw new InvalidImageContentException($"Input stream does not have enough bytes to parse declared contents of the {marker:X2} marker.");
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowBadQuantizationTableIndex(int index) => throw new InvalidImageContentException($"Bad Quantization Table index {index}."); public static void ThrowBadQuantizationTableIndex(int index) => throw new InvalidImageContentException($"Bad Quantization Table index {index}.");

9
src/ImageSharp/IO/BufferedReadStream.cs

@ -114,6 +114,15 @@ namespace SixLabors.ImageSharp.IO
/// <inheritdoc/> /// <inheritdoc/>
public override bool CanWrite { get; } = false; public override bool CanWrite { get; } = false;
/// <summary>
/// Gets remaining byte count available to read.
/// </summary>
public long RemainingBytes
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.Length - this.Position;
}
/// <summary> /// <summary>
/// Gets the underlying stream. /// Gets the underlying stream.
/// </summary> /// </summary>

1
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs

@ -105,6 +105,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693A, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693A,
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693B, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693B,
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C,
TestImages.Jpeg.Issues.Fuzz.NullReferenceException2085,
}; };
private static readonly Dictionary<string, float> CustomToleranceValues = new() private static readonly Dictionary<string, float> CustomToleranceValues = new()

1
tests/ImageSharp.Tests/TestImages.cs

@ -292,6 +292,7 @@ namespace SixLabors.ImageSharp.Tests
public const string AccessViolationException922 = "Jpg/issues/fuzz/Issue922-AccessViolationException.jpg"; public const string AccessViolationException922 = "Jpg/issues/fuzz/Issue922-AccessViolationException.jpg";
public const string IndexOutOfRangeException1693A = "Jpg/issues/fuzz/Issue1693-IndexOutOfRangeException-A.jpg"; public const string IndexOutOfRangeException1693A = "Jpg/issues/fuzz/Issue1693-IndexOutOfRangeException-A.jpg";
public const string IndexOutOfRangeException1693B = "Jpg/issues/fuzz/Issue1693-IndexOutOfRangeException-B.jpg"; public const string IndexOutOfRangeException1693B = "Jpg/issues/fuzz/Issue1693-IndexOutOfRangeException-B.jpg";
public const string NullReferenceException2085 = "Jpg/issues/fuzz/Issue2085-NullReferenceException.jpg";
} }
} }

3
tests/Images/Input/Jpg/issues/fuzz/Issue2085-NullReferenceException.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d478beff34179fda26238a44434607c276f55438ee96824c5af8c0188d358d8d
size 234
Loading…
Cancel
Save