Browse Source

Merge branch 'main' into sn/xunitv3

pull/3175/head
James Jackson-South 2 weeks ago
committed by GitHub
parent
commit
27d1f27524
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs
  2. 4
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs
  3. 12
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs
  4. 4
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs
  5. 10
      src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
  6. 2
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  7. 59
      tests/ImageSharp.Tests/Formats/Tiff/Compression/CcittTiffCompressionTests.cs

11
src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs

@ -53,6 +53,12 @@ internal sealed class ModifiedHuffmanTiffCompression : TiffBaseDecompressor
if (bitReader.RunLength > 0)
{
// A decoded run is untrusted and must fit the current row before the unchecked bit writer is used.
if (bitReader.RunLength > (nuint)this.Width - pixelsWritten)
{
TiffThrowHelper.ThrowImageFormatException("CCITT compression parsing error: decoded more pixels than the image width.");
}
if (bitReader.IsWhiteRun)
{
BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.whiteValue);
@ -86,11 +92,6 @@ internal sealed class ModifiedHuffmanTiffCompression : TiffBaseDecompressor
bitReader.StartNewRow();
}
if (pixelsWritten > (ulong)this.Width)
{
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, decoded more pixels then image width");
}
}
}

4
src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs

@ -319,7 +319,7 @@ internal class T4BitReader
{
if (this.CurValueBitsRead > this.maxCodeLength)
{
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: invalid code length read");
TiffThrowHelper.ThrowImageFormatException("CCITT compression parsing error: invalid code length read.");
}
bool isMakeupCode = this.IsMakeupCode();
@ -401,7 +401,7 @@ internal class T4BitReader
if (!this.IsEndOfScanLine)
{
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: expected start of data marker not found");
TiffThrowHelper.ThrowImageFormatException("CCITT compression parsing error: expected start of data marker not found.");
}
this.Reset();

12
src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs

@ -72,7 +72,7 @@ internal sealed class T4TiffCompression : TiffBaseDecompressor
if (bitReader.RunLength > 0)
{
this.WritePixelRun(buffer, bitReader, bitsWritten);
this.WritePixelRun(buffer, bitReader, bitsWritten, pixelsWritten);
bitsWritten += (int)bitReader.RunLength;
pixelsWritten += bitReader.RunLength;
@ -102,12 +102,18 @@ internal sealed class T4TiffCompression : TiffBaseDecompressor
if (pixelsWritten > 0 && pixelsWritten < (ulong)this.width)
{
bitReader.ReadNextRun();
this.WritePixelRun(buffer, bitReader, bitsWritten);
this.WritePixelRun(buffer, bitReader, bitsWritten, pixelsWritten);
}
}
private void WritePixelRun(Span<byte> buffer, T4BitReader bitReader, nint bitsWritten)
private void WritePixelRun(Span<byte> buffer, T4BitReader bitReader, nint bitsWritten, nuint pixelsWritten)
{
// A decoded run is untrusted and must fit the current row before the unchecked bit writer is used.
if (bitReader.RunLength > (nuint)this.width - pixelsWritten)
{
TiffThrowHelper.ThrowImageFormatException("CCITT compression parsing error: decoded more pixels than the image width.");
}
if (bitReader.IsWhiteRun)
{
BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.whiteValue);

4
src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs

@ -163,7 +163,7 @@ internal sealed class T6TiffCompression : TiffBaseDecompressor
int runLength = (int)bitReader.RunLength;
if (runLength > (uint)(scanline.Length - unpacked))
{
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error");
TiffThrowHelper.ThrowImageFormatException("CCITT compression parsing error: decoded more pixels than the image width.");
}
scanline.Slice(unpacked, runLength).Fill(fillByte);
@ -175,7 +175,7 @@ internal sealed class T6TiffCompression : TiffBaseDecompressor
runLength = (int)bitReader.RunLength;
if (runLength > (uint)(scanline.Length - unpacked))
{
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error");
TiffThrowHelper.ThrowImageFormatException("CCITT compression parsing error: decoded more pixels than the image width.");
}
scanline.Slice(unpacked, runLength).Fill(fillByte);

10
src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs

@ -31,6 +31,10 @@ internal static class TiffDecompressorsFactory
int tileWidth = 0,
int tileHeight = 0)
{
// Fax compression emits one decoded row at a time, so tiled images must use
// the tile row width that was used to size the caller's destination buffer.
int faxWidth = isTiled ? tileWidth : width;
switch (method)
{
case TiffDecoderCompressionType.None:
@ -53,15 +57,15 @@ internal static class TiffDecompressorsFactory
case TiffDecoderCompressionType.T4:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
return new T4TiffCompression(allocator, fillOrder, width, bitsPerPixel, faxOptions, photometricInterpretation);
return new T4TiffCompression(allocator, fillOrder, faxWidth, bitsPerPixel, faxOptions, photometricInterpretation);
case TiffDecoderCompressionType.T6:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
return new T6TiffCompression(allocator, fillOrder, width, bitsPerPixel, photometricInterpretation);
return new T6TiffCompression(allocator, fillOrder, faxWidth, bitsPerPixel, photometricInterpretation);
case TiffDecoderCompressionType.HuffmanRle:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
return new ModifiedHuffmanTiffCompression(allocator, fillOrder, width, bitsPerPixel, photometricInterpretation);
return new ModifiedHuffmanTiffCompression(allocator, fillOrder, faxWidth, bitsPerPixel, photometricInterpretation);
case TiffDecoderCompressionType.Jpeg:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");

2
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -693,7 +693,7 @@ internal class TiffDecoderCore : ImageDecoderCore
tilesBuffers[i] = this.memoryAllocator.Allocate<byte>(uncompressedTilesSize, AllocationOptions.Clean);
}
using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(frame.Width, bitsPerPixel, frame.Metadata);
using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(frame.Width, bitsPerPixel, frame.Metadata, true, tileWidth, tileLength);
TiffBasePlanarColorDecoder<TPixel> colorDecoder = this.CreatePlanarColorDecoder<TPixel>(frame.Metadata);
int tileIndex = 0;

59
tests/ImageSharp.Tests/Formats/Tiff/Compression/CcittTiffCompressionTests.cs

@ -0,0 +1,59 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.IO.Compression;
using SixLabors.ImageSharp.Formats;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression;
[Trait("Format", "Tiff")]
public class CcittTiffCompressionTests
{
private const string T4PocGzipBase64 =
"H4sICBOogmoCA3BvYy10aWZmLXQ0LnRpZgDty7ENwjAQhlFfjKKUoYGSPlPQZopskGVYiJFgA2zpFGUG9Kz3S19xXtelTKX0xaVEq2dbZPcNUY+u2bVtzO7vmvd72+3095792vJyfsQH" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgH33fP9Zj5xoBYAEA";
private const string ModifiedHuffmanPocGzipBase64 =
"H4sICBSogmoCA3BvYy10aWZmLW1oLnRpZgDty7ENgzAQhlEfRBElNKRMnylomSIbZBkWYiTYAFs6RZkhetb7pa84r+urDKW0xa1EraUustu66L/dZ3d19+z2prz/1M0/fx/Z2zsvx2cc" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+jcL3Buw0IBYAEA";
private const string TiledT4ReporterPocGzipBase64 =
"H4sIAAAAAAACCu3UKw7CUBCG0RkuIeC6AxQGj8TwEHWshyWxOhI2QC8Z0QWAIDnNmeQXran4xnEf64jYROQyMvo8RtYepltk++x+rXabblW7P6fZ++fZvtS+T3et/djVR8M2n/Btr1v6C/zCQbQQLUQLRAvRQrRAtBAtRAtEC9FCtEC0EC3+Mlpv/6RrkiQmAAA=";
[Theory]
[InlineData(T4PocGzipBase64)]
[InlineData(ModifiedHuffmanPocGzipBase64)]
public void Decode_CcittStripWithOversizedRun_ThrowsImageFormatException(string gzipBase64)
{
using MemoryStream tiffStream = CreateTiffStream(gzipBase64);
ImageFormatException exception = Assert.Throws<ImageFormatException>(() => Image.Load(tiffStream));
Assert.Equal("CCITT compression parsing error: decoded more pixels than the image width.", exception.Message);
}
[Fact]
public void Decode_TiledCcittReporterSample_ThrowsImageFormatException()
{
// This is the reporter's exact tiled T4 TIFF sample, gzip-compressed only to keep the test source small.
using MemoryStream tiffStream = CreateTiffStream(TiledT4ReporterPocGzipBase64);
ImageFormatException exception = Assert.Throws<ImageFormatException>(() => Image.Load(tiffStream));
Assert.Equal("CCITT compression parsing error: decoded more pixels than the image width.", exception.Message);
}
private static MemoryStream CreateTiffStream(string gzipBase64)
{
// Keep the TIFF payloads compressed so the regression source remains small.
byte[] compressed = Convert.FromBase64String(gzipBase64);
using MemoryStream compressedStream = new(compressed);
using GZipStream gzipStream = new(compressedStream, CompressionMode.Decompress);
MemoryStream tiffStream = new();
gzipStream.CopyTo(tiffStream);
tiffStream.Position = 0;
return tiffStream;
}
}
Loading…
Cancel
Save