diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs
index dc95ca0443..0f97c404bd 100644
--- a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs
+++ b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs
@@ -258,6 +258,9 @@ internal static class WebpChunkParsingUtils
/// The stream to read from.
/// The buffer to store the read data into.
/// A unsigned 24 bit integer.
+ ///
+ /// Thrown if the input stream is not valid.
+ ///
public static uint ReadUInt24LittleEndian(Stream stream, Span buffer)
{
if (stream.Read(buffer, 0, 3) == 3)
@@ -274,6 +277,9 @@ internal static class WebpChunkParsingUtils
///
/// The stream to read from.
/// The uint24 data to write.
+ ///
+ /// Thrown if the data is not a valid unsigned 24 bit integer.
+ ///
public static unsafe void WriteUInt24LittleEndian(Stream stream, uint data)
{
if (data >= 1 << 24)
@@ -296,18 +302,24 @@ internal static class WebpChunkParsingUtils
///
/// The stream to read the data from.
/// Buffer to store the data read from the stream.
+ /// If true, the chunk size is required to be read, otherwise it can be skipped.
/// The chunk size in bytes.
- public static uint ReadChunkSize(Stream stream, Span buffer)
+ /// Thrown if the input stream is not valid.
+ public static uint ReadChunkSize(Stream stream, Span buffer, bool required = true)
{
- DebugGuard.IsTrue(buffer.Length is 4, "buffer has wrong length");
-
if (stream.Read(buffer) is 4)
{
uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
return chunkSize % 2 is 0 ? chunkSize : chunkSize + 1;
}
- throw new ImageFormatException("Invalid Webp data, could not read chunk size.");
+ if (required)
+ {
+ throw new ImageFormatException("Invalid Webp data, could not read chunk size.");
+ }
+
+ // Return the size of the remaining data in the stream.
+ return (uint)(stream.Length - stream.Position);
}
///
@@ -320,14 +332,12 @@ internal static class WebpChunkParsingUtils
///
public static WebpChunkType ReadChunkType(BufferedReadStream stream, Span buffer)
{
- DebugGuard.IsTrue(buffer.Length == 4, "buffer has wrong length");
-
if (stream.Read(buffer) == 4)
{
- WebpChunkType chunkType = (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer);
- return chunkType;
+ return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer);
}
+ // We should ignore unknown chunks but still be able to read the type.
throw new ImageFormatException("Invalid Webp data, could not read chunk type.");
}
@@ -336,6 +346,12 @@ internal static class WebpChunkParsingUtils
/// If there are more such chunks, readers MAY ignore all except the first one.
/// Also, a file may possibly contain both 'EXIF' and 'XMP ' chunks.
///
+ /// The stream to read the data from.
+ /// The chunk type to parse.
+ /// The image metadata to write to.
+ /// If true, metadata will be ignored.
+ /// Indicates how to handle segment integrity issues.
+ /// Buffer to store the data read from the stream.
public static void ParseOptionalChunks(
BufferedReadStream stream,
WebpChunkType chunkType,
@@ -344,10 +360,13 @@ internal static class WebpChunkParsingUtils
SegmentIntegrityHandling segmentIntegrityHandling,
Span buffer)
{
+ bool ignoreNone = segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone;
long streamLength = stream.Length;
while (stream.Position < streamLength)
{
- uint chunkLength = ReadChunkSize(stream, buffer);
+ // Ignore unknown chunk types or when metadata is to be ignored.
+ // If handling should ignore none, we still need to validate the chunk length.
+ uint chunkLength = ReadChunkSize(stream, buffer, ignoreNone && (chunkType is WebpChunkType.Exif or WebpChunkType.Xmp) && !ignoreMetaData);
if (ignoreMetaData)
{
@@ -362,7 +381,7 @@ internal static class WebpChunkParsingUtils
bytesRead = stream.Read(exifData, 0, (int)chunkLength);
if (bytesRead != chunkLength)
{
- if (segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone)
+ if (ignoreNone)
{
WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the EXIF profile");
}
@@ -394,7 +413,7 @@ internal static class WebpChunkParsingUtils
bytesRead = stream.Read(xmpData, 0, (int)chunkLength);
if (bytesRead != chunkLength)
{
- if (segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone)
+ if (ignoreNone)
{
WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the XMP profile");
}
diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
index 0e9888adb2..2d06d0e49e 100644
--- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
@@ -248,7 +248,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
else
{
// Ignore unknown chunks.
- uint chunkSize = ReadChunkSize(stream, buffer, false);
+ uint chunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, false);
stream.Skip((int)chunkSize);
}
}
@@ -328,7 +328,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
while (stream.Position < streamLength)
{
// Read chunk header.
- WebpChunkType chunkType = ReadChunkType(stream, buffer);
+ WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer);
if (chunkType == WebpChunkType.Exif && metadata.ExifProfile == null)
{
this.ReadExifProfile(stream, metadata, buffer);
@@ -340,7 +340,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
else
{
// Skip duplicate XMP or EXIF chunk.
- uint chunkLength = ReadChunkSize(stream, buffer);
+ uint chunkLength = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, false);
stream.Skip((int)chunkLength);
}
}
@@ -354,8 +354,11 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
/// Temporary buffer.
private void ReadExifProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer)
{
- uint exifChunkSize = ReadChunkSize(stream, buffer);
- if (this.skipMetadata)
+ bool ignoreMetadata = this.skipMetadata;
+ bool ignoreNone = this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone && !ignoreMetadata;
+
+ uint exifChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, ignoreNone);
+ if (ignoreMetadata)
{
stream.Skip((int)exifChunkSize);
}
@@ -365,7 +368,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
int bytesRead = stream.Read(exifData, 0, (int)exifChunkSize);
if (bytesRead != exifChunkSize)
{
- if (this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone)
+ if (ignoreNone)
{
WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the EXIF profile");
}
@@ -408,8 +411,11 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
/// Temporary buffer.
private void ReadXmpProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer)
{
- uint xmpChunkSize = ReadChunkSize(stream, buffer);
- if (this.skipMetadata)
+ bool ignoreMetadata = this.skipMetadata;
+ bool ignoreNone = this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone && !ignoreMetadata;
+
+ uint xmpChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, ignoreNone);
+ if (ignoreMetadata)
{
stream.Skip((int)xmpChunkSize);
}
@@ -419,7 +425,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
int bytesRead = stream.Read(xmpData, 0, (int)xmpChunkSize);
if (bytesRead != xmpChunkSize)
{
- if (this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone)
+ if (ignoreNone)
{
WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the XMP profile");
}
@@ -439,7 +445,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
/// Temporary buffer.
private void ReadIccProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer)
{
- uint iccpChunkSize = ReadChunkSize(stream, buffer);
+ uint iccpChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer);
if (this.skipMetadata)
{
stream.Skip((int)iccpChunkSize);
@@ -512,50 +518,6 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
}
}
- ///
- /// Identifies the chunk type from the chunk.
- ///
- /// The stream to decode from.
- /// Temporary buffer.
- ///
- /// Thrown if the input stream is not valid.
- ///
- private static WebpChunkType ReadChunkType(BufferedReadStream stream, Span buffer)
- {
- if (stream.Read(buffer, 0, 4) == 4)
- {
- return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer);
- }
-
- throw new ImageFormatException("Invalid Webp data.");
- }
-
- ///
- /// Reads the chunk size. If Chunk Size is odd, a single padding byte will be added to the payload,
- /// so the chunk size will be increased by 1 in those cases.
- ///
- /// The stream to decode from.
- /// Temporary buffer.
- /// If true, the chunk size is required to be read, otherwise it can be skipped.
- /// The chunk size in bytes.
- /// Invalid data.
- private static uint ReadChunkSize(BufferedReadStream stream, Span buffer, bool required = true)
- {
- if (stream.Read(buffer, 0, 4) == 4)
- {
- uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
- return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1;
- }
-
- if (required)
- {
- throw new ImageFormatException("Invalid Webp data.");
- }
-
- // Return the size of the remaining data in the stream.
- return (uint)(stream.Length - stream.Position);
- }
-
///
public void Dispose() => this.alphaData?.Dispose();
}