diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 52a52c472b..074cd14ac3 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -4,6 +4,7 @@ on:
push:
branches:
- main
+ - release/*
tags:
- "v*"
pull_request:
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index f3a6228d81..6a321a3ba0 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -126,6 +126,11 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
///
private readonly Crc32 crc32 = new();
+ ///
+ /// The maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed.
+ ///
+ private readonly int maxUncompressedLength;
+
///
/// Initializes a new instance of the class.
///
@@ -138,6 +143,7 @@ public PngDecoderCore(PngDecoderOptions options)
this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.memoryAllocator = this.configuration.MemoryAllocator;
this.pngCrcChunkHandling = options.PngCrcChunkHandling;
+ this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes;
}
internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
@@ -149,6 +155,7 @@ internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
this.pngCrcChunkHandling = options.PngCrcChunkHandling;
+ this.maxUncompressedLength = options.MaxUncompressedAncillaryChunkSizeBytes;
}
///
@@ -602,23 +609,7 @@ private static void ReadGammaChunk(PngMetadata pngMetadata, ReadOnlySpan d
private void InitializeImage(ImageMetadata metadata, FrameControl frameControl, out Image image)
where TPixel : unmanaged, IPixel
{
- // When ignoring data CRCs, we can't use the image constructor that leaves the buffer uncleared.
- if (this.pngCrcChunkHandling is PngCrcChunkHandling.IgnoreData or PngCrcChunkHandling.IgnoreAll)
- {
- image = new Image(
- this.configuration,
- this.header.Width,
- this.header.Height,
- metadata);
- }
- else
- {
- image = Image.CreateUninitialized(
- this.configuration,
- this.header.Width,
- this.header.Height,
- metadata);
- }
+ image = new Image(this.configuration, this.header.Width, this.header.Height, metadata);
PngFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetPngMetadata();
frameMetadata.FromChunk(in frameControl);
@@ -1575,7 +1566,7 @@ private void ReadColorProfileChunk(ImageMetadata metadata, ReadOnlySpan da
ReadOnlySpan compressedData = data[(zeroIndex + 2)..];
- if (this.TryDecompressZlibData(compressedData, out byte[] iccpProfileBytes))
+ if (this.TryDecompressZlibData(compressedData, this.maxUncompressedLength, out byte[] iccpProfileBytes))
{
metadata.IccProfile = new IccProfile(iccpProfileBytes);
}
@@ -1585,9 +1576,10 @@ private void ReadColorProfileChunk(ImageMetadata metadata, ReadOnlySpan da
/// Tries to decompress zlib compressed data.
///
/// The compressed data.
+ /// The maximum uncompressed length.
/// The uncompressed bytes array.
/// True, if de-compressing was successful.
- private unsafe bool TryDecompressZlibData(ReadOnlySpan compressedData, out byte[] uncompressedBytesArray)
+ private unsafe bool TryDecompressZlibData(ReadOnlySpan compressedData, int maxLength, out byte[] uncompressedBytesArray)
{
fixed (byte* compressedDataBase = compressedData)
{
@@ -1607,6 +1599,12 @@ private unsafe bool TryDecompressZlibData(ReadOnlySpan compressedData, out
int bytesRead = inflateStream.CompressedStream.Read(destUncompressedData, 0, destUncompressedData.Length);
while (bytesRead != 0)
{
+ if (memoryStreamOutput.Length > maxLength)
+ {
+ uncompressedBytesArray = Array.Empty();
+ return false;
+ }
+
memoryStreamOutput.Write(destUncompressedData[..bytesRead]);
bytesRead = inflateStream.CompressedStream.Read(destUncompressedData, 0, destUncompressedData.Length);
}
@@ -1749,7 +1747,7 @@ private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpanThe .
private bool TryDecompressTextData(ReadOnlySpan compressedData, Encoding encoding, [NotNullWhen(true)] out string? value)
{
- if (this.TryDecompressZlibData(compressedData, out byte[] uncompressedData))
+ if (this.TryDecompressZlibData(compressedData, this.maxUncompressedLength, out byte[] uncompressedData))
{
value = encoding.GetString(uncompressedData);
return true;
diff --git a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs
index ab6ba4770e..abfa4b1da8 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderOptions.cs
@@ -15,4 +15,10 @@ public sealed class PngDecoderOptions : ISpecializedDecoderOptions
/// Gets a value indicating how to handle validation of any CRC (Cyclic Redundancy Check) data within the encoded PNG.
///
public PngCrcChunkHandling PngCrcChunkHandling { get; init; } = PngCrcChunkHandling.IgnoreNonCritical;
+
+ ///
+ /// Gets the maximum memory in bytes that a zTXt, sPLT, iTXt, iCCP, or unknown chunk can occupy when decompressed.
+ /// Defaults to 8MB
+ ///
+ public int MaxUncompressedAncillaryChunkSizeBytes { get; init; } = 8 * 1024 * 1024; // 8MB
}
diff --git a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
index 63571617fb..eccd9ede8e 100644
--- a/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/AlphaDecoder.cs
@@ -6,7 +6,9 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Webp.BitReader;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Memory;
@@ -311,8 +313,7 @@ private static void ColorIndexInverseTransformAlpha(
private static void HorizontalUnfilter(Span prev, Span input, Span dst, int width)
{
- // TODO: Investigate AdvSimd support for this method.
- if (Sse2.IsSupported && width >= 9)
+ if ((Sse2.IsSupported || AdvSimd.IsSupported) && width >= 9)
{
dst[0] = (byte)(input[0] + (prev.IsEmpty ? 0 : prev[0]));
nuint i;
@@ -323,17 +324,17 @@ private static void HorizontalUnfilter(Span prev, Span input, Span a0 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref srcRef, i)), 0);
- Vector128 a1 = Sse2.Add(a0.AsByte(), last.AsByte());
- Vector128 a2 = Sse2.ShiftLeftLogical128BitLane(a1, 1);
- Vector128 a3 = Sse2.Add(a1, a2);
- Vector128 a4 = Sse2.ShiftLeftLogical128BitLane(a3, 2);
- Vector128 a5 = Sse2.Add(a3, a4);
- Vector128 a6 = Sse2.ShiftLeftLogical128BitLane(a5, 4);
- Vector128 a7 = Sse2.Add(a5, a6);
+ Vector128 a1 = a0.AsByte() + last.AsByte();
+ Vector128 a2 = Vector128Utilities.ShiftLeftBytesInVector(a1, 1);
+ Vector128 a3 = a1 + a2;
+ Vector128 a4 = Vector128Utilities.ShiftLeftBytesInVector(a3, 2);
+ Vector128 a5 = a3 + a4;
+ Vector128 a6 = Vector128Utilities.ShiftLeftBytesInVector(a5, 4);
+ Vector128 a7 = a5 + a6;
ref byte outputRef = ref Unsafe.Add(ref dstRef, i);
Unsafe.As>(ref outputRef) = a7.GetLower();
- last = Sse2.ShiftRightLogical(a7.AsInt64(), 56).AsInt32();
+ last = Vector128.ShiftRightLogical(a7.AsInt64(), 56).AsInt32();
}
for (; i < (uint)width; ++i)
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
index 9f41d5fba2..de99432bce 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
@@ -672,4 +672,23 @@ public void Decode_Issue2666()
string path = Path.GetFullPath(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Png.Issue2666));
using Image image = Image.Load(path);
}
+
+ [Theory]
+
+ [InlineData(TestImages.Png.Bad.BadZTXT)]
+ [InlineData(TestImages.Png.Bad.BadZTXT2)]
+ public void Decode_BadZTXT(string file)
+ {
+ string path = Path.GetFullPath(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, file));
+ using Image image = Image.Load(path);
+ }
+
+ [Theory]
+ [InlineData(TestImages.Png.Bad.BadZTXT)]
+ [InlineData(TestImages.Png.Bad.BadZTXT2)]
+ public void Info_BadZTXT(string file)
+ {
+ string path = Path.GetFullPath(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, file));
+ _ = Image.Identify(path);
+ }
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 022c3654a8..ac70792681 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -186,8 +186,10 @@ public static class Bad
// Invalid color type.
public const string ColorTypeOne = "Png/xc1n0g08.png";
public const string ColorTypeNine = "Png/xc9n2c08.png";
-
public const string FlagOfGermany0000016446 = "Png/issues/flag_of_germany-0000016446.png";
+
+ public const string BadZTXT = "Png/issues/bad-ztxt.png";
+ public const string BadZTXT2 = "Png/issues/bad-ztxt2.png";
}
}
diff --git a/tests/Images/Input/Png/issues/bad-ztxt.png b/tests/Images/Input/Png/issues/bad-ztxt.png
new file mode 100644
index 0000000000..710f888d0b
--- /dev/null
+++ b/tests/Images/Input/Png/issues/bad-ztxt.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:132a70cf0ac458a55cf4a44f4c6c025587491d304595835959955de6682fa472
+size 3913750
diff --git a/tests/Images/Input/Png/issues/bad-ztxt2.png b/tests/Images/Input/Png/issues/bad-ztxt2.png
new file mode 100644
index 0000000000..958c00e3f0
--- /dev/null
+++ b/tests/Images/Input/Png/issues/bad-ztxt2.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:778a5fc8e915d79e9f55e58c6e4f646ae55dd7e866e65960754cb67a2b445987
+size 93