@@ -114,27 +114,34 @@ internal sealed class PngDecoderCore : IImageDecoderInternals
114
114
/// </summary>
115
115
private PngChunk ? nextChunk ;
116
116
117
+ /// <summary>
118
+ /// How to handle CRC errors.
119
+ /// </summary>
120
+ private readonly PngCrcChunkHandling pngCrcChunkHandling ;
121
+
117
122
/// <summary>
118
123
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
119
124
/// </summary>
120
125
/// <param name="options">The decoder options.</param>
121
- public PngDecoderCore ( DecoderOptions options )
126
+ public PngDecoderCore ( PngDecoderOptions options )
122
127
{
123
- this . Options = options ;
124
- this . configuration = options . Configuration ;
125
- this . maxFrames = options . MaxFrames ;
126
- this . skipMetadata = options . SkipMetadata ;
128
+ this . Options = options . GeneralOptions ;
129
+ this . configuration = options . GeneralOptions . Configuration ;
130
+ this . maxFrames = options . GeneralOptions . MaxFrames ;
131
+ this . skipMetadata = options . GeneralOptions . SkipMetadata ;
127
132
this . memoryAllocator = this . configuration . MemoryAllocator ;
133
+ this . pngCrcChunkHandling = options . PngCrcChunkHandling ;
128
134
}
129
135
130
- internal PngDecoderCore ( DecoderOptions options , bool colorMetadataOnly )
136
+ internal PngDecoderCore ( PngDecoderOptions options , bool colorMetadataOnly )
131
137
{
132
- this . Options = options ;
138
+ this . Options = options . GeneralOptions ;
133
139
this . colorMetadataOnly = colorMetadataOnly ;
134
- this . maxFrames = options . MaxFrames ;
140
+ this . maxFrames = options . GeneralOptions . MaxFrames ;
135
141
this . skipMetadata = true ;
136
- this . configuration = options . Configuration ;
142
+ this . configuration = options . GeneralOptions . Configuration ;
137
143
this . memoryAllocator = this . configuration . MemoryAllocator ;
144
+ this . pngCrcChunkHandling = options . PngCrcChunkHandling ;
138
145
}
139
146
140
147
/// <inheritdoc/>
@@ -576,11 +583,23 @@ private static void ReadGammaChunk(PngMetadata pngMetadata, ReadOnlySpan<byte> d
576
583
private void InitializeImage < TPixel > ( ImageMetadata metadata , FrameControl frameControl , out Image < TPixel > image )
577
584
where TPixel : unmanaged, IPixel < TPixel >
578
585
{
579
- image = Image . CreateUninitialized < TPixel > (
580
- this . configuration ,
581
- this . header . Width ,
582
- this . header . Height ,
583
- metadata ) ;
586
+ // When ignoring data CRCs, we can't use the image constructor that leaves the buffer uncleared.
587
+ if ( this . pngCrcChunkHandling is PngCrcChunkHandling . IgnoreData or PngCrcChunkHandling . IgnoreAll )
588
+ {
589
+ image = new Image < TPixel > (
590
+ this . configuration ,
591
+ this . header . Width ,
592
+ this . header . Height ,
593
+ metadata ) ;
594
+ }
595
+ else
596
+ {
597
+ image = Image . CreateUninitialized < TPixel > (
598
+ this . configuration ,
599
+ this . header . Width ,
600
+ this . header . Height ,
601
+ metadata ) ;
602
+ }
584
603
585
604
PngFrameMetadata frameMetadata = image . Frames . RootFrame . Metadata . GetPngMetadata ( ) ;
586
605
frameMetadata . FromChunk ( in frameControl ) ;
@@ -803,6 +822,11 @@ private void DecodePixelData<TPixel>(
803
822
break ;
804
823
805
824
default :
825
+ if ( this . pngCrcChunkHandling is PngCrcChunkHandling . IgnoreData or PngCrcChunkHandling . IgnoreAll )
826
+ {
827
+ goto EXIT ;
828
+ }
829
+
806
830
PngThrowHelper . ThrowUnknownFilter ( ) ;
807
831
break ;
808
832
}
@@ -812,6 +836,7 @@ private void DecodePixelData<TPixel>(
812
836
currentRow ++ ;
813
837
}
814
838
839
+ EXIT :
815
840
blendMemory ? . Dispose ( ) ;
816
841
}
817
842
@@ -903,6 +928,11 @@ private void DecodeInterlacedPixelData<TPixel>(
903
928
break ;
904
929
905
930
default :
931
+ if ( this . pngCrcChunkHandling is PngCrcChunkHandling . IgnoreData or PngCrcChunkHandling . IgnoreAll )
932
+ {
933
+ goto EXIT ;
934
+ }
935
+
906
936
PngThrowHelper . ThrowUnknownFilter ( ) ;
907
937
break ;
908
938
}
@@ -937,6 +967,7 @@ private void DecodeInterlacedPixelData<TPixel>(
937
967
}
938
968
}
939
969
970
+ EXIT :
940
971
blendMemory ? . Dispose ( ) ;
941
972
}
942
973
@@ -1364,7 +1395,7 @@ private void ReadCompressedTextChunk(ImageMetadata baseMetadata, PngMetadata met
1364
1395
1365
1396
ReadOnlySpan < byte > compressedData = data [ ( zeroIndex + 2 ) ..] ;
1366
1397
1367
- if ( this . TryUncompressTextData ( compressedData , PngConstants . Encoding , out string ? uncompressed )
1398
+ if ( this . TryDecompressTextData ( compressedData , PngConstants . Encoding , out string ? uncompressed )
1368
1399
&& ! TryReadTextChunkMetadata ( baseMetadata , name , uncompressed ) )
1369
1400
{
1370
1401
metadata . TextData . Add ( new PngTextData ( name , uncompressed , string . Empty , string . Empty ) ) ;
@@ -1508,19 +1539,19 @@ private void ReadColorProfileChunk(ImageMetadata metadata, ReadOnlySpan<byte> da
1508
1539
1509
1540
ReadOnlySpan < byte > compressedData = data [ ( zeroIndex + 2 ) ..] ;
1510
1541
1511
- if ( this . TryUncompressZlibData ( compressedData , out byte [ ] iccpProfileBytes ) )
1542
+ if ( this . TryDecompressZlibData ( compressedData , out byte [ ] iccpProfileBytes ) )
1512
1543
{
1513
1544
metadata . IccProfile = new IccProfile ( iccpProfileBytes ) ;
1514
1545
}
1515
1546
}
1516
1547
1517
1548
/// <summary>
1518
- /// Tries to un-compress zlib compressed data.
1549
+ /// Tries to decompress zlib compressed data.
1519
1550
/// </summary>
1520
1551
/// <param name="compressedData">The compressed data.</param>
1521
1552
/// <param name="uncompressedBytesArray">The uncompressed bytes array.</param>
1522
1553
/// <returns>True, if de-compressing was successful.</returns>
1523
- private unsafe bool TryUncompressZlibData ( ReadOnlySpan < byte > compressedData , out byte [ ] uncompressedBytesArray )
1554
+ private unsafe bool TryDecompressZlibData ( ReadOnlySpan < byte > compressedData , out byte [ ] uncompressedBytesArray )
1524
1555
{
1525
1556
fixed ( byte * compressedDataBase = compressedData )
1526
1557
{
@@ -1657,7 +1688,7 @@ private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan<byt
1657
1688
{
1658
1689
ReadOnlySpan < byte > compressedData = data [ dataStartIdx ..] ;
1659
1690
1660
- if ( this . TryUncompressTextData ( compressedData , PngConstants . TranslatedEncoding , out string ? uncompressed ) )
1691
+ if ( this . TryDecompressTextData ( compressedData , PngConstants . TranslatedEncoding , out string ? uncompressed ) )
1661
1692
{
1662
1693
pngMetadata . TextData . Add ( new PngTextData ( keyword , uncompressed , language , translatedKeyword ) ) ;
1663
1694
}
@@ -1680,9 +1711,9 @@ private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan<byt
1680
1711
/// <param name="encoding">The string encoding to use.</param>
1681
1712
/// <param name="value">The uncompressed value.</param>
1682
1713
/// <returns>The <see cref="bool"/>.</returns>
1683
- private bool TryUncompressTextData ( ReadOnlySpan < byte > compressedData , Encoding encoding , [ NotNullWhen ( true ) ] out string ? value )
1714
+ private bool TryDecompressTextData ( ReadOnlySpan < byte > compressedData , Encoding encoding , [ NotNullWhen ( true ) ] out string ? value )
1684
1715
{
1685
- if ( this . TryUncompressZlibData ( compressedData , out byte [ ] uncompressedData ) )
1716
+ if ( this . TryDecompressZlibData ( compressedData , out byte [ ] uncompressedData ) )
1686
1717
{
1687
1718
value = encoding . GetString ( uncompressedData ) ;
1688
1719
return true ;
@@ -1705,7 +1736,11 @@ private int ReadNextDataChunk()
1705
1736
1706
1737
Span < byte > buffer = stackalloc byte [ 20 ] ;
1707
1738
1708
- _ = this . currentStream . Read ( buffer , 0 , 4 ) ;
1739
+ int length = this . currentStream . Read ( buffer , 0 , 4 ) ;
1740
+ if ( length == 0 )
1741
+ {
1742
+ return 0 ;
1743
+ }
1709
1744
1710
1745
if ( this . TryReadChunk ( buffer , out PngChunk chunk ) )
1711
1746
{
@@ -1734,7 +1769,11 @@ private int ReadNextFrameDataChunk()
1734
1769
1735
1770
Span < byte > buffer = stackalloc byte [ 20 ] ;
1736
1771
1737
- _ = this . currentStream . Read ( buffer , 0 , 4 ) ;
1772
+ int length = this . currentStream . Read ( buffer , 0 , 4 ) ;
1773
+ if ( length == 0 )
1774
+ {
1775
+ return 0 ;
1776
+ }
1738
1777
1739
1778
if ( this . TryReadChunk ( buffer , out PngChunk chunk ) )
1740
1779
{
@@ -1771,21 +1810,27 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
1771
1810
return true ;
1772
1811
}
1773
1812
1774
- if ( ! this . TryReadChunkLength ( buffer , out int length ) )
1813
+ if ( this . currentStream . Position >= this . currentStream . Length - 1 )
1775
1814
{
1815
+ // IEND
1776
1816
chunk = default ;
1817
+ return false ;
1818
+ }
1777
1819
1820
+ if ( ! this . TryReadChunkLength ( buffer , out int length ) )
1821
+ {
1778
1822
// IEND
1823
+ chunk = default ;
1779
1824
return false ;
1780
1825
}
1781
1826
1782
- while ( length < 0 || length > ( this . currentStream . Length - this . currentStream . Position ) )
1827
+ while ( length < 0 )
1783
1828
{
1784
1829
// Not a valid chunk so try again until we reach a known chunk.
1785
1830
if ( ! this . TryReadChunkLength ( buffer , out length ) )
1786
1831
{
1832
+ // IEND
1787
1833
chunk = default ;
1788
-
1789
1834
return false ;
1790
1835
}
1791
1836
}
@@ -1797,13 +1842,14 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
1797
1842
if ( this . colorMetadataOnly && type != PngChunkType . Header && type != PngChunkType . Transparency && type != PngChunkType . Palette )
1798
1843
{
1799
1844
chunk = new PngChunk ( length , type ) ;
1800
-
1801
1845
return true ;
1802
1846
}
1803
1847
1804
- long pos = this . currentStream . Position ;
1848
+ // A chunk might report a length that exceeds the length of the stream.
1849
+ // Take the minimum of the two values to ensure we don't read past the end of the stream.
1850
+ long position = this . currentStream . Position ;
1805
1851
chunk = new PngChunk (
1806
- length : length ,
1852
+ length : ( int ) Math . Min ( length , this . currentStream . Length - position ) ,
1807
1853
type : type ,
1808
1854
data : this . ReadChunkData ( length ) ) ;
1809
1855
@@ -1813,7 +1859,7 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
1813
1859
// was only read to verifying the CRC is correct.
1814
1860
if ( type is PngChunkType . Data or PngChunkType . FrameData )
1815
1861
{
1816
- this . currentStream . Position = pos ;
1862
+ this . currentStream . Position = position ;
1817
1863
}
1818
1864
1819
1865
return true ;
@@ -1827,8 +1873,7 @@ private bool TryReadChunk(Span<byte> buffer, out PngChunk chunk)
1827
1873
private void ValidateChunk ( in PngChunk chunk , Span < byte > buffer )
1828
1874
{
1829
1875
uint inputCrc = this . ReadChunkCrc ( buffer ) ;
1830
-
1831
- if ( chunk . IsCritical )
1876
+ if ( chunk . IsCritical ( this . pngCrcChunkHandling ) )
1832
1877
{
1833
1878
Span < byte > chunkType = stackalloc byte [ 4 ] ;
1834
1879
BinaryPrimitives . WriteUInt32BigEndian ( chunkType , ( uint ) chunk . Type ) ;
0 commit comments