Browse Source

Handle incomplete EXR zlib headers

pull/3187/head
James Jackson-South 3 weeks ago
parent
commit
b11f2eb647
  1. 10
      src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
  2. 43
      tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs

10
src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs

@ -53,8 +53,14 @@ internal abstract class ExrBaseDecompressor : ExrBaseCompression
int left = (int)(compressedBytes - (stream.Position - pos)); int left = (int)(compressedBytes - (stream.Position - pos));
return left > 0 ? left : 0; return left > 0 ? left : 0;
}); });
inflateStream.AllocateNewBytes((int)compressedBytes, true);
using DeflateStream dataStream = inflateStream.CompressedStream!; // Incomplete headers return false even for critical chunks, leaving no stream to read.
if (!inflateStream.AllocateNewBytes((int)compressedBytes, true))
{
ExrThrowHelper.ThrowInvalidImageContentException("ZIP compressed EXR block has an incomplete zlib header.");
}
using DeflateStream dataStream = inflateStream.CompressedStream;
int totalRead = 0; int totalRead = 0;
while (totalRead < uncompressedBytes) while (totalRead < uncompressedBytes)

43
tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs

@ -67,6 +67,42 @@ public class ExrZipDecoderTests
} }
} }
/// <summary>
/// Missing or truncated zlib headers obey the image-data integrity policy.
/// </summary>
/// <param name="length">The number of available zlib header bytes.</param>
/// <param name="integrity">The image-data integrity policy.</param>
[Theory]
[InlineData(0, SegmentIntegrityHandling.Strict)]
[InlineData(1, SegmentIntegrityHandling.Strict)]
[InlineData(0, SegmentIntegrityHandling.IgnoreAncillary)]
[InlineData(1, SegmentIntegrityHandling.IgnoreAncillary)]
[InlineData(0, SegmentIntegrityHandling.IgnoreImageData)]
[InlineData(1, SegmentIntegrityHandling.IgnoreImageData)]
public void Decode_IncompleteZlibHeader_RespectsIntegrityHandling(int length, SegmentIntegrityHandling integrity)
{
byte[] header = [0x78, 0x9C];
byte[] data = BuildExr(header[..length], ExrPixelType.Float, 2, 0);
Configuration configuration = Configuration.Default.Clone();
configuration.MemoryAllocator = new TestMemoryAllocator(0x3F);
DecoderOptions options = new() { Configuration = configuration, SegmentIntegrityHandling = integrity };
if (integrity == SegmentIntegrityHandling.IgnoreImageData)
{
using Image<RgbaVector> image = Image.Load<RgbaVector>(options, data);
Assert.Equal(new Size(256, 1), image.Size);
for (int x = 0; x < image.Width; x++)
{
Assert.Equal(new Vector4(0, 0, 0, 1), image[x, 0].ToVector4());
}
}
else
{
Assert.Throws<InvalidImageContentException>(() => Image.Load<RgbaVector>(options, data));
}
}
/// <summary> /// <summary>
/// Missing color channels must not inherit the allocator's previous contents. /// Missing color channels must not inherit the allocator's previous contents.
/// </summary> /// </summary>
@ -113,6 +149,13 @@ public class ExrZipDecoderTests
/// <returns>The zlib stream.</returns> /// <returns>The zlib stream.</returns>
private static byte[] ZlibCompress(byte[] data) private static byte[] ZlibCompress(byte[] data)
{ {
if (data.Length == 0)
{
// An empty write produces no output on some runtimes. Use a complete zlib stream
// containing an empty final DEFLATE block and Adler-32 checksum instead.
return [0x78, 0x9C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01];
}
using MemoryStream output = new(); using MemoryStream output = new();
using (ZLibStream zlib = new(output, CompressionLevel.Optimal, leaveOpen: true)) using (ZLibStream zlib = new(output, CompressionLevel.Optimal, leaveOpen: true))
{ {

Loading…
Cancel
Save