diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index 34028c2f83..c9f4258feb 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -58,7 +58,7 @@ internal sealed unsafe partial class JpegEncoderCore
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
- if (image.Width >= JpegConstants.MaxLength || image.Height >= JpegConstants.MaxLength)
+ if (image.Width > JpegConstants.MaxLength || image.Height > JpegConstants.MaxLength)
{
JpegThrowHelper.ThrowDimensionsTooLarge(image.Width, image.Height);
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index b0ec73d2a1..0971c3ecfc 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -131,6 +131,11 @@ internal sealed class PngDecoderCore : ImageDecoderCore
///
private readonly int maxUncompressedLength;
+ ///
+ /// A value indicating whether the image data has been read.
+ ///
+ private bool hasImageData;
+
///
/// Initializes a new instance of the class.
///
@@ -749,7 +754,11 @@ internal sealed class PngDecoderCore : ImageDecoderCore
where TPixel : unmanaged, IPixel
{
using ZlibInflateStream inflateStream = new(this.currentStream, getData);
- inflateStream.AllocateNewBytes(chunkLength, true);
+ if (!inflateStream.AllocateNewBytes(chunkLength, !this.hasImageData))
+ {
+ return;
+ }
+
DeflateStream dataStream = inflateStream.CompressedStream!;
if (this.header.InterlaceMethod is PngInterlaceMode.Adam7)
@@ -803,7 +812,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
int bytesRead = compressedStream.Read(scanSpan, currentRowBytesRead, bytesPerFrameScanline - currentRowBytesRead);
if (bytesRead <= 0)
{
- return;
+ goto EXIT;
}
currentRowBytesRead += bytesRead;
@@ -848,6 +857,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
}
EXIT:
+ this.hasImageData = true;
blendMemory?.Dispose();
}
@@ -906,7 +916,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
int bytesRead = compressedStream.Read(this.scanline.GetSpan(), currentRowBytesRead, bytesPerInterlaceScanline - currentRowBytesRead);
if (bytesRead <= 0)
{
- return;
+ goto EXIT;
}
currentRowBytesRead += bytesRead;
@@ -979,6 +989,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
}
EXIT:
+ this.hasImageData = true;
blendMemory?.Dispose();
}
diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
index f088448391..1864e539c1 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
@@ -371,9 +371,6 @@ internal class Vp8LEncoder : IDisposable
/// The input image height.
private void WriteImageSize(int inputImgWidth, int inputImgHeight)
{
- Guard.MustBeLessThan(inputImgWidth, WebpConstants.MaxDimension, nameof(inputImgWidth));
- Guard.MustBeLessThan(inputImgHeight, WebpConstants.MaxDimension, nameof(inputImgHeight));
-
uint width = (uint)inputImgWidth - 1;
uint height = (uint)inputImgHeight - 1;
diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
index 9ca6e2bee3..51379a32ae 100644
--- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
@@ -220,7 +220,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
else
{
// Ignore unknown chunks.
- uint chunkSize = ReadChunkSize(stream, buffer);
+ uint chunkSize = ReadChunkSize(stream, buffer, false);
stream.Skip((int)chunkSize);
}
}
@@ -498,9 +498,10 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
///
/// 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)
+ private static uint ReadChunkSize(BufferedReadStream stream, Span buffer, bool required = true)
{
if (stream.Read(buffer, 0, 4) == 4)
{
@@ -508,7 +509,13 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable
return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1;
}
- throw new ImageFormatException("Invalid Webp data.");
+ if (required)
+ {
+ throw new ImageFormatException("Invalid Webp data.");
+ }
+
+ // Return the size of the remaining data in the stream.
+ return (uint)(stream.Length - stream.Position);
}
///
diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
index b3270786d7..788a90a829 100644
--- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
@@ -132,6 +132,11 @@ internal sealed class WebpEncoderCore
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
+ if (image.Width > WebpConstants.MaxDimension || image.Height > WebpConstants.MaxDimension)
+ {
+ WebpThrowHelper.ThrowDimensionsTooLarge(image.Width, image.Height);
+ }
+
bool lossless;
if (this.fileFormat is not null)
{
diff --git a/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs b/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
index c633c52738..d730953829 100644
--- a/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
+++ b/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
@@ -18,4 +18,7 @@ internal static class WebpThrowHelper
[DoesNotReturn]
public static void ThrowInvalidImageDimensions(string errorMessage) => throw new InvalidImageContentException(errorMessage);
+
+ [DoesNotReturn]
+ public static void ThrowDimensionsTooLarge(int width, int height) => throw new ImageFormatException($"Image is too large to encode at {width}x{height} for WEBP format.");
}
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
index 9f3c5f6828..4d058e54e8 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
@@ -719,10 +719,20 @@ public partial class PngDecoderTests
[Theory]
[WithFile(TestImages.Png.Issue2752, PixelTypes.Rgba32)]
public void CanDecodeJustOneFrame(TestImageProvider provider)
- where TPixel : unmanaged, IPixel
+ where TPixel : unmanaged, IPixel
{
DecoderOptions options = new() { MaxFrames = 1 };
using Image image = provider.GetImage(PngDecoder.Instance, options);
Assert.Equal(1, image.Frames.Count);
}
+
+ [Theory]
+ [WithFile(TestImages.Png.Issue2924, PixelTypes.Rgba32)]
+ public void CanDecode_Issue2924(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage(PngDecoder.Instance);
+ image.DebugSave(provider);
+ image.CompareToReferenceOutput(provider);
+ }
}
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
index adabb727d8..660ab2e666 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
@@ -549,4 +549,14 @@ public class WebpDecoderTests
Assert.Equal(37.8, meta.VerticalResolution);
Assert.Equal(PixelResolutionUnit.PixelsPerCentimeter, meta.ResolutionUnits);
}
+
+ [Theory]
+ [WithFile(Lossy.Issue2925, PixelTypes.Rgba32)]
+ public void WebpDecoder_CanDecode_Issue2925(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage(WebpDecoder.Instance);
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider, ReferenceDecoder);
+ }
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index a5336e52fb..8b5529ac1a 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -160,6 +160,9 @@ public static class TestImages
// Issue 2752: https://github.com/SixLabors/ImageSharp/issues/2752
public const string Issue2752 = "Png/issues/Issue_2752.png";
+ // Issue 2924: https://github.com/SixLabors/ImageSharp/issues/2924
+ public const string Issue2924 = "Png/issues/Issue_2924.png";
+
public static class Bad
{
public const string MissingDataChunk = "Png/xdtn0g01.png";
@@ -871,6 +874,7 @@ public static class TestImages
public const string Issue2763 = "Webp/issues/Issue2763.png";
public const string Issue2801 = "Webp/issues/Issue2801.webp";
public const string Issue2866 = "Webp/issues/Issue2866.webp";
+ public const string Issue2925 = "Webp/issues/Issue2925.webp";
}
public const string AlphaBlend = "Webp/alpha-blend.webp";
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
index 263df8f3a6..994aa670c7 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
@@ -59,11 +59,6 @@ public static class TestImageExtensions
bool appendSourceFileOrDescription = true,
IImageEncoder encoder = null)
{
- if (TestEnvironment.RunsWithCodeCoverage)
- {
- return image;
- }
-
provider.Utility.SaveTestOutputFile(
image,
extension,
@@ -112,11 +107,6 @@ public static class TestImageExtensions
Func predicate = null)
where TPixel : unmanaged, IPixel
{
- if (TestEnvironment.RunsWithCodeCoverage)
- {
- return image;
- }
-
provider.Utility.SaveTestOutputFileMultiFrame(
image,
extension,
@@ -298,24 +288,23 @@ public static class TestImageExtensions
appendPixelTypeToFileName,
predicate: predicate);
- using (Image debugImage = GetDebugOutputImageMultiFrame(
+ using Image debugImage = GetDebugOutputImageMultiFrame(
provider,
image.Frames.Count,
testOutputDetails,
extension,
appendPixelTypeToFileName,
- predicate: predicate))
+ predicate: predicate);
- using (Image referenceImage = GetReferenceOutputImageMultiFrame(
+ using Image referenceImage = GetReferenceOutputImageMultiFrame(
provider,
image.Frames.Count,
testOutputDetails,
extension,
appendPixelTypeToFileName,
- predicate: predicate))
- {
- comparer.VerifySimilarity(referenceImage, debugImage);
- }
+ predicate: predicate);
+
+ comparer.VerifySimilarity(referenceImage, debugImage);
return image;
}
diff --git a/tests/Images/External/ReferenceOutput/PngDecoderTests/CanDecode_Issue2924_Rgba32_Issue_2924.png b/tests/Images/External/ReferenceOutput/PngDecoderTests/CanDecode_Issue2924_Rgba32_Issue_2924.png
new file mode 100644
index 0000000000..023f346e03
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/PngDecoderTests/CanDecode_Issue2924_Rgba32_Issue_2924.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4347cd89196c09496288724afdd876b227063149bba33615c338ebb474a0cb19
+size 47260
diff --git a/tests/Images/Input/Png/issues/Issue_2924.png b/tests/Images/Input/Png/issues/Issue_2924.png
new file mode 100644
index 0000000000..0454642190
--- /dev/null
+++ b/tests/Images/Input/Png/issues/Issue_2924.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fd366a2de522041c706729b01a6030df6c82a4e87ea3509cc78a47486f097044
+size 49376
diff --git a/tests/Images/Input/Webp/issues/Issue2925.webp b/tests/Images/Input/Webp/issues/Issue2925.webp
new file mode 100644
index 0000000000..414a06caad
--- /dev/null
+++ b/tests/Images/Input/Webp/issues/Issue2925.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e12207babf122af7a62246938c2e78faa0d3f730edb3182f4f9d6adae6bfc602
+size 262144