Browse Source

Check Stream.Read return value

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
d146069bc5
  1. 137
      src/ImageSharp/Formats/Heif/HeifDecoderCore.cs

137
src/ImageSharp/Formats/Heif/HeifDecoderCore.cs

@ -34,8 +34,6 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private readonly List<HeifItemLink> itemLinks;
private readonly byte[] buffer;
/// <summary>
/// Initializes a new instance of the <see cref="HeifDecoderCore" /> class.
/// </summary>
@ -47,7 +45,6 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
this.metadata = new ImageMetadata();
this.items = new List<HeifItem>();
this.itemLinks = new List<HeifItemLink>();
this.buffer = new byte[80];
}
/// <inheritdoc/>
@ -140,8 +137,9 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private bool CheckFileTypeBox(BufferedReadStream stream)
{
long boxLength = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
uint majorBrand = BinaryPrimitives.ReadUInt32BigEndian(buffer);
using IMemoryOwner<byte> boxMemory = this.ReadIntoBuffer(stream, boxLength);
Span<byte> boxBuffer = boxMemory.GetSpan();
uint majorBrand = BinaryPrimitives.ReadUInt32BigEndian(boxBuffer);
bool correctBrand = majorBrand is (uint)Heif4CharCode.Heic or (uint)Heif4CharCode.Heix or (uint)Heif4CharCode.Avif;
// TODO: Interpret minorVersion and compatible brands.
@ -218,7 +216,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
while (stream.Position < endPosition)
{
long length = this.ReadBoxHeader(stream, out Heif4CharCode boxType);
EnsureBoxBoundary(length, boxLength);
EnsureBoxInsideParent(length, boxLength);
switch (boxType)
{
case Heif4CharCode.Iprp:
@ -256,11 +254,12 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private void ParseHandler(BufferedReadStream stream, long boxLength)
{
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
using IMemoryOwner<byte> boxMemory = this.ReadIntoBuffer(stream, boxLength);
Span<byte> boxBuffer = boxMemory.GetSpan();
// Only read the handler type, to check if this is not a movie file.
int bytesRead = 8;
Heif4CharCode handlerType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer[bytesRead..]);
Heif4CharCode handlerType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[bytesRead..]);
if (handlerType != Heif4CharCode.Pict)
{
throw new ImageFormatException("Not a picture file.");
@ -269,16 +268,17 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private void ParseItemInfo(BufferedReadStream stream, long boxLength)
{
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
using IMemoryOwner<byte> boxMemory = this.ReadIntoBuffer(stream, boxLength);
Span<byte> boxBuffer = boxMemory.GetSpan();
uint entryCount;
int bytesRead = 0;
byte version = buffer[bytesRead];
byte version = boxBuffer[bytesRead];
bytesRead += 4;
entryCount = ReadUInt16Or32(buffer, version != 0, ref bytesRead);
entryCount = ReadUInt16Or32(boxBuffer, version != 0, ref bytesRead);
for (uint i = 0; i < entryCount; i++)
{
bytesRead += this.ParseItemInfoEntry(buffer[bytesRead..]);
bytesRead += this.ParseItemInfoEntry(boxBuffer[bytesRead..]);
}
}
@ -364,21 +364,22 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private void ParseItemReference(BufferedReadStream stream, long boxLength)
{
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
using IMemoryOwner<byte> boxMemory = this.ReadIntoBuffer(stream, boxLength);
Span<byte> boxBuffer = boxMemory.GetSpan();
int bytesRead = 0;
bool largeIds = buffer[bytesRead] != 0;
bool largeIds = boxBuffer[bytesRead] != 0;
bytesRead += 4;
while (bytesRead < boxLength)
{
bytesRead += ParseBoxHeader(buffer[bytesRead..], out long subBoxLength, out Heif4CharCode linkType);
uint sourceId = ReadUInt16Or32(buffer, largeIds, ref bytesRead);
bytesRead += ParseBoxHeader(boxBuffer[bytesRead..], out long subBoxLength, out Heif4CharCode linkType);
uint sourceId = ReadUInt16Or32(boxBuffer, largeIds, ref bytesRead);
HeifItemLink link = new(linkType, sourceId);
int count = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
int count = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]);
bytesRead += 2;
for (uint i = 0; i < count; i++)
{
uint destId = ReadUInt16Or32(buffer, largeIds, ref bytesRead);
uint destId = ReadUInt16Or32(boxBuffer, largeIds, ref bytesRead);
link.DestinationIds.Add(destId);
}
@ -389,10 +390,11 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private void ParsePrimaryItem(BufferedReadStream stream, long boxLength)
{
// BoxLength should be 6 or 8.
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
byte version = buffer[0];
using IMemoryOwner<byte> boxMemory = this.ReadIntoBuffer(stream, boxLength);
Span<byte> boxBuffer = boxMemory.GetSpan();
byte version = boxBuffer[0];
int bytesRead = 4;
this.primaryItem = ReadUInt16Or32(buffer, version != 0, ref bytesRead);
this.primaryItem = ReadUInt16Or32(boxBuffer, version != 0, ref bytesRead);
}
private void ParseItemProperties(BufferedReadStream stream, long boxLength)
@ -403,7 +405,7 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
while (stream.Position < endBoxPosition)
{
long containerLength = this.ReadBoxHeader(stream, out Heif4CharCode containerType);
EnsureBoxBoundary(containerLength, boxLength);
EnsureBoxInsideParent(containerLength, boxLength);
if (containerType == Heif4CharCode.Ipco)
{
// Parse Item Property Container, which is just an array of property boxes.
@ -427,40 +429,41 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
while (stream.Position < endPosition)
{
int itemLength = (int)this.ReadBoxHeader(stream, out Heif4CharCode itemType);
EnsureBoxBoundary(itemLength, boxLength);
Span<byte> buffer = this.ReadIntoBuffer(stream, itemLength);
EnsureBoxInsideParent(itemLength, boxLength);
using IMemoryOwner<byte> boxMemory = this.ReadIntoBuffer(stream, itemLength);
Span<byte> boxBuffer = boxMemory.GetSpan();
switch (itemType)
{
case Heif4CharCode.Ispe:
// Skip over version (8 bits) and flags (24 bits).
int width = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]);
int height = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[8..]);
int width = (int)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[4..]);
int height = (int)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[8..]);
properties.Add(new KeyValuePair<Heif4CharCode, object>(Heif4CharCode.Ispe, new Size(width, height)));
break;
case Heif4CharCode.Pasp:
int horizontalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer);
int verticalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(buffer[4..]);
int horizontalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer);
int verticalSpacing = (int)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer[4..]);
properties.Add(new KeyValuePair<Heif4CharCode, object>(Heif4CharCode.Pasp, new Size(horizontalSpacing, verticalSpacing)));
break;
case Heif4CharCode.Pixi:
// Skip over version (8 bits) and flags (24 bits).
int channelCount = buffer[4];
int channelCount = boxBuffer[4];
int offset = 5;
int bitsPerPixel = 0;
for (int i = 0; i < channelCount; i++)
{
bitsPerPixel += buffer[offset + i];
bitsPerPixel += boxBuffer[offset + i];
}
properties.Add(new KeyValuePair<Heif4CharCode, object>(Heif4CharCode.Pixi, new int[] { channelCount, bitsPerPixel }));
break;
case Heif4CharCode.Colr:
Heif4CharCode profileType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(buffer);
Heif4CharCode profileType = (Heif4CharCode)BinaryPrimitives.ReadUInt32BigEndian(boxBuffer);
if (profileType is Heif4CharCode.RICC or Heif4CharCode.Prof)
{
byte[] iccData = new byte[itemLength - 4];
buffer[4..].CopyTo(iccData);
boxBuffer[4..].CopyTo(iccData);
this.metadata.IccProfile = new IccProfile(iccData);
}
@ -484,24 +487,25 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private void ParsePropertyAssociation(BufferedReadStream stream, long boxLength, List<KeyValuePair<Heif4CharCode, object>> properties)
{
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
byte version = buffer[0];
byte flags = buffer[3];
using IMemoryOwner<byte> boxMemory = this.ReadIntoBuffer(stream, boxLength);
Span<byte> boxBuffer = boxMemory.GetSpan();
byte version = boxBuffer[0];
byte flags = boxBuffer[3];
int bytesRead = 4;
int itemId = (int)ReadUInt16Or32(buffer, version >= 1, ref bytesRead);
int itemId = (int)ReadUInt16Or32(boxBuffer, version >= 1, ref bytesRead);
int associationCount = buffer[bytesRead++];
int associationCount = boxBuffer[bytesRead++];
for (int i = 0; i < associationCount; i++)
{
uint propId;
if (flags == 1)
{
propId = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]) & 0x4FFFU;
propId = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]) & 0x4FFFU;
bytesRead += 2;
}
else
{
propId = buffer[bytesRead++] & 0x4FU;
propId = boxBuffer[bytesRead++] & 0x4FU;
}
KeyValuePair<Heif4CharCode, object> prop = properties[(int)propId];
@ -524,12 +528,13 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private void ParseItemLocation(BufferedReadStream stream, long boxLength)
{
Span<byte> buffer = this.ReadIntoBuffer(stream, boxLength);
using IMemoryOwner<byte> boxMemory = this.ReadIntoBuffer(stream, boxLength);
Span<byte> boxBuffer = boxMemory.GetSpan();
int bytesRead = 0;
byte version = buffer[bytesRead];
byte version = boxBuffer[bytesRead];
bytesRead += 4;
byte b1 = buffer[bytesRead++];
byte b2 = buffer[bytesRead++];
byte b1 = boxBuffer[bytesRead++];
byte b2 = boxBuffer[bytesRead++];
int offsetSize = (b1 >> 4) & 0x0f;
int lengthSize = b1 & 0x0f;
int baseOffsetSize = (b2 >> 4) & 0x0f;
@ -539,32 +544,32 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
indexSize = b2 & 0x0f;
}
uint itemCount = ReadUInt16Or32(buffer, version == 2, ref bytesRead);
uint itemCount = ReadUInt16Or32(boxBuffer, version == 2, ref bytesRead);
for (uint i = 0; i < itemCount; i++)
{
uint itemId = ReadUInt16Or32(buffer, version == 2, ref bytesRead);
uint itemId = ReadUInt16Or32(boxBuffer, version == 2, ref bytesRead);
HeifItem? item = this.FindItemById(itemId);
if (version is 1 or 2)
{
bytesRead++;
byte b3 = buffer[bytesRead++];
byte b3 = boxBuffer[bytesRead++];
int constructionMethod = b3 & 0x0f;
}
uint dataReferenceIndex = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
uint dataReferenceIndex = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]);
bytesRead += 2;
ulong baseOffset = ReadUIntVariable(buffer, baseOffsetSize, ref bytesRead);
uint extentCount = BinaryPrimitives.ReadUInt16BigEndian(buffer[bytesRead..]);
ulong baseOffset = ReadUIntVariable(boxBuffer, baseOffsetSize, ref bytesRead);
uint extentCount = BinaryPrimitives.ReadUInt16BigEndian(boxBuffer[bytesRead..]);
bytesRead += 2;
for (uint j = 0; j < extentCount; j++)
{
if (version is 1 or 2 && indexSize > 0)
{
_ = ReadUIntVariable(buffer, indexSize, ref bytesRead);
_ = ReadUIntVariable(boxBuffer, indexSize, ref bytesRead);
}
ulong extentOffset = ReadUIntVariable(buffer, offsetSize, ref bytesRead);
ulong extentLength = ReadUIntVariable(buffer, lengthSize, ref bytesRead);
ulong extentOffset = ReadUIntVariable(boxBuffer, offsetSize, ref bytesRead);
ulong extentLength = ReadUIntVariable(boxBuffer, lengthSize, ref bytesRead);
HeifLocation loc = new HeifLocation((long)extentOffset, (long)extentLength);
item?.DataLocations.Add(loc);
}
@ -617,7 +622,8 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private Image<TPixel> ParseMediaData<TPixel>(BufferedReadStream stream, long boxLength)
where TPixel : unmanaged, IPixel<TPixel>
{
// FIXME: No HVC decoding yet, so parse only a JPEG thumbnail.
EnsureBoxBoundary(boxLength, stream);
// FIXME: No specific decoding yet, so parse only a JPEG thumbnail.
HeifItemLink? thumbLink = this.itemLinks.FirstOrDefault(link => link.Type == Heif4CharCode.Thmb);
if (thumbLink == null)
{
@ -633,9 +639,9 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
int thumbFileOffset = (int)thumbItem.DataLocations[0].Offset;
int thumbFileLength = (int)thumbItem.DataLocations[0].Length;
stream.Skip((int)(thumbFileOffset - stream.Position));
using IMemoryOwner<byte> thumbMemory = this.configuration.MemoryAllocator.Allocate<byte>(thumbFileLength);
EnsureBoxBoundary(thumbFileLength, stream);
using IMemoryOwner<byte> thumbMemory = this.ReadIntoBuffer(stream, thumbFileLength);
Span<byte> thumbSpan = thumbMemory.GetSpan();
stream.Read(thumbSpan);
HeifMetadata meta = this.metadata.GetHeifMetadata();
meta.CompressionMethod = HeifCompressionMethod.LegacyJpeg;
@ -646,25 +652,22 @@ internal sealed class HeifDecoderCore : IImageDecoderInternals
private static void SkipBox(BufferedReadStream stream, long boxLength)
=> stream.Skip((int)boxLength);
private Span<byte> ReadIntoBuffer(BufferedReadStream stream, long length)
private IMemoryOwner<byte> ReadIntoBuffer(BufferedReadStream stream, long length)
{
if (length <= this.buffer.Length)
IMemoryOwner<byte> buffer = this.configuration.MemoryAllocator.Allocate<byte>((int)length);
int bytesRead = stream.Read(buffer.GetSpan());
if (bytesRead != length)
{
int bytesRead = stream.Read(this.buffer, 0, (int)length);
return this.buffer.AsSpan(0, bytesRead);
}
else
{
Span<byte> temp = new byte[length];
int bytesRead = stream.Read(temp);
return temp;
throw new InvalidImageContentException("Stream length not sufficient for box content");
}
return buffer;
}
private static void EnsureBoxBoundary(long boxLength, Stream stream)
=> EnsureBoxBoundary(boxLength, stream.Length - stream.Position);
=> EnsureBoxInsideParent(boxLength, stream.Length - stream.Position);
private static void EnsureBoxBoundary(long boxLength, long parentLength)
private static void EnsureBoxInsideParent(long boxLength, long parentLength)
{
if (boxLength > parentLength)
{

Loading…
Cancel
Save