@@ -13,6 +13,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Exr;
1313/// The EXR decoder was merged to main but not yet included in a tagged NuGet release.
1414/// Each test demonstrates a crafted-input crash present in the unfixed code.
1515/// </summary>
16+ [ Trait ( "Format" , "Exr" ) ]
17+ [ ValidateDisposedMemoryAllocations ]
1618public class ExrDecoderSecurityTests
1719{
1820 /// <summary>
@@ -109,31 +111,52 @@ public void Decode_CraftedRowOffsets_IntoOffsetTable_Throws()
109111 }
110112
111113 /// <summary>
112- /// EXR-3 — EXR bytesPerBlock uint Overflow Chain (DoS)
114+ /// EXR-3 — Oversized EXR RGBA row sizing is rejected as invalid image content.
113115 ///
114- /// CalculateBytesPerRow is computed in ulong (fixed), and if the result exceeds
115- /// int.MaxValue the decoder throws InvalidImageContentException. With 4 RGBA HALF
116- /// channels and Width = 2^29:
117- /// bytesPerRow = 4 × 2 × 2^29 = 2^32 (> int.MaxValue)
118- /// → InvalidImageContentException before any allocation
116+ /// With 4 RGBA HALF channels and Width = 2^29, the decoded row staging and
117+ /// bytes-per-row arithmetic both exceed the supported buffer sizing limits.
118+ /// The decoder must reject this as InvalidImageContentException before any allocation.
119119 ///
120120 /// Affected file:
121121 /// src/ImageSharp/Formats/Exr/ExrDecoderCore.cs lines 142–150, 215–223
122122 /// src/ImageSharp/Formats/Exr/ExrUtils.cs CalculateBytesPerRow
123123 /// </summary>
124124 [ Fact ]
125- public void Decode_BytesPerBlockUintOverflow_Throws ( )
125+ public void Decode_RgbaRowSizingExceedsBufferLimits_Throws ( )
126126 {
127- // 4 RGBA HALF channels, Width = 2^29:
128- // bytesPerRow = 4 × 2 × 536870912 = 4294967296 > int.MaxValue
129- // → InvalidImageContentException from the block-size guard
127+ // 4 RGBA HALF channels at this width cannot be represented by the decoder's
128+ // int-sized row staging or block buffers.
130129 byte [ ] data = BuildMinimalRgbaExr ( xMin : 0 , yMin : 0 , xMax : 536870911 , yMax : 0 ) ;
131130
132131 using var stream = new MemoryStream ( data ) ;
133132 Assert . Throws < InvalidImageContentException > (
134133 ( ) => ExrDecoder . Instance . Decode < Rgba32 > ( DecoderOptions . Default , stream ) ) ;
135134 }
136135
136+ [ Fact ]
137+ public void Decode_DataWindowWidthExceedsRowBufferLimit_Throws ( )
138+ {
139+ // A single HALF channel keeps bytesPerBlock below int.MaxValue, but the decoder
140+ // still stages four color planes and must reject widths that overflow width × 4.
141+ byte [ ] data = BuildMinimalExr ( xMin : 0 , yMin : 0 , xMax : int . MaxValue / 4 , yMax : 0 ) ;
142+
143+ using var stream = new MemoryStream ( data ) ;
144+ Assert . Throws < InvalidImageContentException > (
145+ ( ) => ExrDecoder . Instance . Decode < Rgba32 > ( DecoderOptions . Default , stream ) ) ;
146+ }
147+
148+ [ Fact ]
149+ public void Identify_RowOffsetTableExceedsStream_Throws ( )
150+ {
151+ // Identify parses the header only, so this verifies the offset table bound is
152+ // validated before scanline decoding reads from the table.
153+ byte [ ] data = BuildMinimalExr ( xMin : 0 , yMin : 0 , xMax : 1 , yMax : 1 ) ;
154+
155+ using var stream = new MemoryStream ( data ) ;
156+ Assert . Throws < InvalidImageContentException > (
157+ ( ) => ExrDecoder . Instance . Identify ( DecoderOptions . Default , stream ) ) ;
158+ }
159+
137160 // -------------------------------------------------------------------------
138161 // Helpers: construct minimal valid-enough EXR scanline files.
139162 //
@@ -144,7 +167,7 @@ public void Decode_BytesPerBlockUintOverflow_Throws()
144167
145168 private static byte [ ] BuildMinimalExr (
146169 int xMin , int yMin , int xMax , int yMax ,
147- byte [ ] ? rowOffsetTableAppend = null )
170+ byte [ ] rowOffsetTableAppend = null )
148171 {
149172 // channels: single "R" HALF channel with xSampling=1, ySampling=1
150173 // Layout per ReadChannelInfo: name\0 (2) + pixelType (4) + pLinear+reserved (4)
@@ -198,7 +221,7 @@ private static byte[] BuildMinimalRgbaExr(int xMin, int yMin, int xMax, int yMax
198221 private static byte [ ] BuildExrWithChannels (
199222 int xMin , int yMin , int xMax , int yMax ,
200223 byte [ ] channelData ,
201- byte [ ] ? rowOffsetTableAppend = null )
224+ byte [ ] rowOffsetTableAppend = null )
202225 {
203226 using var ms = new MemoryStream ( ) ;
204227 using var bw = new BinaryWriter ( ms , System . Text . Encoding . ASCII , leaveOpen : true ) ;
0 commit comments