Skip to content

Commit efe11dc

Browse files
Fix PNG chunk detection.
1 parent 0ea2404 commit efe11dc

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,6 +1853,9 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
18531853
return false;
18541854
}
18551855

1856+
// Capture the current position so we can revert back to it if we fail to read a valid chunk.
1857+
long position = this.currentStream.Position;
1858+
18561859
if (!this.TryReadChunkLength(buffer, out int length))
18571860
{
18581861
// IEND
@@ -1871,7 +1874,48 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
18711874
}
18721875
}
18731876

1874-
PngChunkType type = this.ReadChunkType(buffer);
1877+
PngChunkType type;
1878+
1879+
// Loop until we get a chunk type that is valid.
1880+
while (true)
1881+
{
1882+
type = this.ReadChunkType(buffer);
1883+
if (!IsValidChunkType(type))
1884+
{
1885+
// The chunk type is invalid.
1886+
// Revert back to the next byte past the previous position and try again.
1887+
this.currentStream.Position = ++position;
1888+
1889+
// If we are now at the end of the stream, we're done.
1890+
if (this.currentStream.Position >= this.currentStream.Length)
1891+
{
1892+
chunk = default;
1893+
return false;
1894+
}
1895+
1896+
// Read the next chunk’s length.
1897+
if (!this.TryReadChunkLength(buffer, out length))
1898+
{
1899+
chunk = default;
1900+
return false;
1901+
}
1902+
1903+
while (length < 0)
1904+
{
1905+
if (!this.TryReadChunkLength(buffer, out length))
1906+
{
1907+
chunk = default;
1908+
return false;
1909+
}
1910+
}
1911+
1912+
// Continue to try reading the next chunk.
1913+
continue;
1914+
}
1915+
1916+
// We have a valid chunk type.
1917+
break;
1918+
}
18751919

18761920
// If we're reading color metadata only we're only interested in the IHDR and tRNS chunks.
18771921
// We can skip most other chunk data in the stream for better performance.
@@ -1888,7 +1932,7 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
18881932

18891933
// A chunk might report a length that exceeds the length of the stream.
18901934
// Take the minimum of the two values to ensure we don't read past the end of the stream.
1891-
long position = this.currentStream.Position;
1935+
position = this.currentStream.Position;
18921936
chunk = new PngChunk(
18931937
length: (int)Math.Min(length, this.currentStream.Length - position),
18941938
type: type,
@@ -1906,6 +1950,32 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
19061950
return true;
19071951
}
19081952

1953+
/// <summary>
1954+
/// Determines whether the 4-byte chunk type is valid (all ASCII letters).
1955+
/// </summary>
1956+
/// <param name="type">The chunk type.</param>
1957+
[MethodImpl(InliningOptions.ShortMethod)]
1958+
private static bool IsValidChunkType(PngChunkType type)
1959+
{
1960+
uint value = (uint)type;
1961+
byte b0 = (byte)(value >> 24);
1962+
byte b1 = (byte)(value >> 16);
1963+
byte b2 = (byte)(value >> 8);
1964+
byte b3 = (byte)value;
1965+
return IsAsciiLetter(b0) && IsAsciiLetter(b1) && IsAsciiLetter(b2) && IsAsciiLetter(b3);
1966+
}
1967+
1968+
/// <summary>
1969+
/// Returns a value indicating whether the given byte is an ASCII letter.
1970+
/// </summary>
1971+
/// <param name="b">The byte to check.</param>
1972+
/// <returns>
1973+
/// <see langword="true"/> if the byte is an ASCII letter; otherwise, <see langword="false"/>.
1974+
/// </returns>
1975+
[MethodImpl(InliningOptions.ShortMethod)]
1976+
private static bool IsAsciiLetter(byte b)
1977+
=> (b >= (byte)'A' && b <= (byte)'Z') || (b >= (byte)'a' && b <= (byte)'z');
1978+
19091979
/// <summary>
19101980
/// Validates the png chunk.
19111981
/// </summary>

tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ public void Issue_2862()
720720
using MemoryStream ms = new();
721721
PaletteQuantizer quantizer = new(
722722
palette.Select(Color.FromPixel).ToArray(),
723-
new QuantizerOptions() { ColorMatchingMode = ColorMatchingMode.Hybrid});
723+
new QuantizerOptions() { ColorMatchingMode = ColorMatchingMode.Hybrid });
724724

725725
image.Save(ms, new PngEncoder
726726
{

0 commit comments

Comments
 (0)