Browse Source

Only read the crc of critical chunks.

pull/1055/head
James Jackson-South 7 years ago
parent
commit
61c6334881
  1. 17
      src/ImageSharp/Formats/Png/PngChunk.cs
  2. 59
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  3. 29
      src/ImageSharp/Formats/Png/PngThrowHelper.cs

17
src/ImageSharp/Formats/Png/PngChunk.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Memory;
@ -10,12 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal readonly struct PngChunk
{
public PngChunk(int length, PngChunkType type, IManagedByteBuffer data = null, uint crc = 0)
public PngChunk(int length, PngChunkType type, IManagedByteBuffer data = null)
{
this.Length = length;
this.Type = type;
this.Data = data;
this.Crc = crc;
}
/// <summary>
@ -38,20 +37,12 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
public IManagedByteBuffer Data { get; }
/// <summary>
/// Gets a CRC (Cyclic Redundancy Check) calculated on the preceding bytes in the chunk,
/// including the chunk type code and chunk data fields, but not including the length field.
/// The CRC is always present, even for chunks containing no data
/// </summary>
public uint Crc { get; }
/// <summary>
/// Gets a value indicating whether the given chunk is critical to decoding
/// </summary>
public bool IsCritical =>
this.Type == PngChunkType.Header ||
this.Type == PngChunkType.Palette ||
this.Type == PngChunkType.Data ||
this.Type == PngChunkType.End;
this.Type == PngChunkType.Data;
}
}
}

59
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -221,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Png
if (image is null)
{
throw new ImageFormatException("PNG Image does not contain a data chunk");
PngThrowHelper.ThrowNoData();
}
return image;
@ -285,7 +285,7 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.header.Width == 0 && this.header.Height == 0)
{
throw new ImageFormatException("PNG Image does not contain a header chunk");
PngThrowHelper.ThrowNoHeader();
}
return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata);
@ -407,7 +407,8 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.RgbWithAlpha:
return this.header.BitDepth * 4;
default:
throw new NotSupportedException("Unsupported PNG color type");
PngThrowHelper.ThrowNotSupportedColor();
return -1;
}
}
@ -528,7 +529,8 @@ namespace SixLabors.ImageSharp.Formats.Png
break;
default:
throw new ImageFormatException("Unknown filter type.");
PngThrowHelper.ThrowUnknownFilter();
break;
}
this.ProcessDefilteredScanline(scanlineSpan, image, pngMetadata);
@ -601,7 +603,8 @@ namespace SixLabors.ImageSharp.Formats.Png
break;
default:
throw new ImageFormatException("Unknown filter type.");
PngThrowHelper.ThrowUnknownFilter();
break;
}
Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow);
@ -1119,13 +1122,9 @@ namespace SixLabors.ImageSharp.Formats.Png
chunk = new PngChunk(
length: length,
type: type,
data: this.ReadChunkData(length),
crc: this.ReadChunkCrc());
data: this.ReadChunkData(length));
if (chunk.IsCritical)
{
this.ValidateChunk(chunk);
}
this.ValidateChunk(chunk);
return true;
}
@ -1136,6 +1135,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="chunk">The <see cref="PngChunk"/>.</param>
private void ValidateChunk(in PngChunk chunk)
{
if (!chunk.IsCritical)
{
return;
}
Span<byte> chunkType = stackalloc byte[4];
BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type);
@ -1144,25 +1148,26 @@ namespace SixLabors.ImageSharp.Formats.Png
this.crc.Update(chunkType);
this.crc.Update(chunk.Data.GetSpan());
if (this.crc.Value != chunk.Crc)
uint crc = this.ReadChunkCrc();
if (this.crc.Value != crc)
{
string chunkTypeName = Encoding.ASCII.GetString(chunkType);
throw new ImageFormatException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!");
PngThrowHelper.ThrowInvalidChunkCrc(chunkTypeName);
}
}
/// <summary>
/// Reads the cycle redundancy chunk from the data.
/// </summary>
/// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid or corrupt.
/// </exception>
private uint ReadChunkCrc()
{
return this.currentStream.Read(this.buffer, 0, 4) == 4
? BinaryPrimitives.ReadUInt32BigEndian(this.buffer)
: throw new ImageFormatException("Image stream is not valid!");
uint crc = 0;
if (this.currentStream.Read(this.buffer, 0, 4) == 4)
{
crc = BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
}
return crc;
}
/// <summary>
@ -1197,9 +1202,17 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </exception>
private PngChunkType ReadChunkType()
{
return this.currentStream.Read(this.buffer, 0, 4) == 4
? (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer)
: throw new ImageFormatException("Invalid PNG data.");
if (this.currentStream.Read(this.buffer, 0, 4) == 4)
{
return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
}
else
{
PngThrowHelper.ThrowInvalidChunkType();
// The IDE cannot detect the throw here.
return default;
}
}
/// <summary>

29
src/ImageSharp/Formats/Png/PngThrowHelper.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Cold path optimizations for throwing png format based exceptions.
/// </summary>
internal static class PngThrowHelper
{
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNoHeader() => throw new ImageFormatException("PNG Image does not contain a header chunk");
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNoData() => throw new ImageFormatException("PNG Image does not contain a data chunk");
public static void ThrowInvalidChunkType() => throw new ImageFormatException("Invalid PNG data.");
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new ImageFormatException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!");
public static void ThrowNotSupportedColor() => new NotSupportedException("Unsupported PNG color type");
public static void ThrowUnknownFilter() => throw new ImageFormatException("Unknown filter type.");
}
}
Loading…
Cancel
Save