Browse Source

sanitize MCU processing code

af/merge-core
Anton Firszov 8 years ago
parent
commit
0fc9002dcc
  1. 8
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs
  2. 205
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs
  3. 9
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

8
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs

@ -380,5 +380,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x); this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x);
return this.LastErrorCode; return this.LastErrorCode;
} }
/// <summary>
/// Reset the Huffman decoder.
/// </summary>
public void ResetHuffmanDecoder()
{
this.Bits = default(Bits);
}
} }
} }

205
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs

@ -94,6 +94,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary> /// </summary>
private int eobRun; private int eobRun;
/// <summary>
/// The block counter
/// </summary>
private int blockCounter;
/// <summary>
/// The MCU counter
/// </summary>
private int mcuCounter;
/// <summary>
/// The expected RST marker value
/// </summary>
private byte expectedRst;
/// <summary> /// <summary>
/// Initializes a default-constructed <see cref="OrigJpegScanDecoder"/> instance for reading data from <see cref="OrigJpegDecoderCore"/>-s stream. /// Initializes a default-constructed <see cref="OrigJpegScanDecoder"/> instance for reading data from <see cref="OrigJpegDecoderCore"/>-s stream.
/// </summary> /// </summary>
@ -139,124 +154,136 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
{ {
decoder.InputProcessor.ResetErrorState(); decoder.InputProcessor.ResetErrorState();
int blockCount = 0; this.blockCounter = 0;
int mcu = 0; this.mcuCounter = 0;
byte expectedRst = OrigJpegConstants.Markers.RST0; this.expectedRst = OrigJpegConstants.Markers.RST0;
for (int my = 0; my < decoder.MCUCountY; my++) for (int my = 0; my < decoder.MCUCountY; my++)
{ {
for (int mx = 0; mx < decoder.MCUCountX; mx++) for (int mx = 0; mx < decoder.MCUCountX; mx++)
{ {
for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) this.DecodeBlocksAtMcuIndex(decoder, mx, my);
{
this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex;
OrigComponent component = decoder.Components[this.ComponentIndex];
this.hi = component.HorizontalSamplingFactor; this.mcuCounter++;
int vi = component.VerticalSamplingFactor;
for (int j = 0; j < this.hi * vi; j++)
{
if (this.componentScanCount != 1)
{
this.bx = (this.hi * mx) + (j % this.hi);
this.by = (vi * my) + (j / this.hi);
}
else
{
int q = decoder.MCUCountX * this.hi;
this.bx = blockCount % q;
this.by = blockCount / q;
blockCount++;
if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight)
{
continue;
}
}
// Find the block at (bx,by) in the component's buffer: // Handling restart intervals
ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by); // Useful info: https://stackoverflow.com/a/8751802
if (decoder.IsAtRestartInterval(this.mcuCounter))
{
this.ProcessRSTMarker(decoder);
this.Reset(decoder);
}
}
}
}
// Copy block to stack private void DecodeBlocksAtMcuIndex(OrigJpegDecoderCore decoder, int mx, int my)
this.data.Block = blockRefOnHeap; {
for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++)
{
this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex;
OrigComponent component = decoder.Components[this.ComponentIndex];
if (!decoder.InputProcessor.ReachedEOF) this.hi = component.HorizontalSamplingFactor;
{ int vi = component.VerticalSamplingFactor;
this.DecodeBlock(decoder, scanIndex);
}
// Store the result block: for (int j = 0; j < this.hi * vi; j++)
blockRefOnHeap = this.data.Block; {
if (this.componentScanCount != 1)
{
this.bx = (this.hi * mx) + (j % this.hi);
this.by = (vi * my) + (j / this.hi);
}
else
{
int q = decoder.MCUCountX * this.hi;
this.bx = this.blockCounter % q;
this.by = this.blockCounter / q;
this.blockCounter++;
if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight)
{
continue;
} }
}
// Find the block at (bx,by) in the component's buffer:
ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by);
// for j // Copy block to stack
this.data.Block = blockRefOnHeap;
if (!decoder.InputProcessor.ReachedEOF)
{
this.DecodeBlock(decoder, scanIndex);
} }
// for i // Store the result block:
mcu++; blockRefOnHeap = this.data.Block;
}
}
}
if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < decoder.TotalMCUCount) private void ProcessRSTMarker(OrigJpegDecoderCore decoder)
{
// Attempt to look for RST[0-7] markers to resynchronize from corrupt input.
if (!decoder.InputProcessor.ReachedEOF)
{
decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2);
if (decoder.InputProcessor.CheckEOFEnsureNoError())
{
if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != this.expectedRst)
{ {
// Attempt to look for RST[0-7] markers to resynchronize from corrupt input. bool invalidRst = true;
if (!decoder.InputProcessor.ReachedEOF)
// Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately
// but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker.
// If we identify that case we attempt to read until we have bypassed the padded bytes.
// We then check again for our RST marker and throw if invalid.
// No other methods are attempted to resynchronize from corrupt input.
if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF)
{ {
decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError())
if (decoder.InputProcessor.CheckEOFEnsureNoError())
{ {
if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != expectedRst) decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1);
{ if (!decoder.InputProcessor.CheckEOFEnsureNoError())
bool invalidRst = true;
// Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately
// but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker.
// If we identify that case we attempt to read until we have bypassed the padded bytes.
// We then check again for our RST marker and throw if invalid.
// No other methods are attempted to resynchronize from corrupt input.
if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF)
{
while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError())
{
decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1);
if (!decoder.InputProcessor.CheckEOFEnsureNoError())
{
break;
}
}
// Have we found a valid restart marker?
invalidRst = decoder.Temp[0] != expectedRst;
}
if (invalidRst)
{
throw new ImageFormatException("Bad RST marker");
}
}
expectedRst++;
if (expectedRst == OrigJpegConstants.Markers.RST7 + 1)
{ {
expectedRst = OrigJpegConstants.Markers.RST0; break;
} }
} }
}
// Reset the Huffman decoder. // Have we found a valid restart marker?
decoder.InputProcessor.Bits = default(Bits); invalidRst = decoder.Temp[0] != this.expectedRst;
}
// Reset the DC components, as per section F.2.1.3.1. if (invalidRst)
this.ResetDc(); {
throw new ImageFormatException("Bad RST marker");
}
}
// Reset the progressive decoder state, as per section G.1.2.2. this.expectedRst++;
this.eobRun = 0; if (this.expectedRst == OrigJpegConstants.Markers.RST7 + 1)
{
this.expectedRst = OrigJpegConstants.Markers.RST0;
} }
} }
// for mx
} }
} }
private void ResetDc() private void Reset(OrigJpegDecoderCore decoder)
{
decoder.InputProcessor.ResetHuffmanDecoder();
this.ResetDcValues();
// Reset the progressive decoder state, as per section G.1.2.2.
this.eobRun = 0;
}
/// <summary>
/// Reset the DC components, as per section F.2.1.3.1.
/// </summary>
private void ResetDcValues()
{ {
Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * OrigJpegDecoderCore.MaxComponents); Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * OrigJpegDecoderCore.MaxComponents);
} }

9
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -411,6 +411,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.InitDerivedMetaDataProperties(); this.InitDerivedMetaDataProperties();
} }
/// <summary>
/// Returns true if 'mcuCounter' is at restart interval
/// </summary>
public bool IsAtRestartInterval(int mcuCounter)
{
return this.RestartInterval > 0 && mcuCounter % this.RestartInterval == 0
&& mcuCounter < this.TotalMCUCount;
}
/// <summary> /// <summary>
/// Assigns derived metadata properties to <see cref="MetaData"/>, eg. horizontal and vertical resolution if it has a JFIF header. /// Assigns derived metadata properties to <see cref="MetaData"/>, eg. horizontal and vertical resolution if it has a JFIF header.
/// </summary> /// </summary>

Loading…
Cancel
Save