@@ -221,6 +221,8 @@ pub(crate) fn box_kernel(_x: f32) -> f32 {
221221// ```new_width``` is the desired width of the new image
222222// ```filter``` is the filter to use for sampling.
223223// ```image``` is not necessarily Rgba and the order of channels is passed through.
224+ //
225+ // Note: if an empty image is passed in, panics unless the image is truly empty.
224226fn horizontal_sample < P , S > (
225227 image : & Rgba32FImage ,
226228 new_width : u32 ,
@@ -231,6 +233,13 @@ where
231233 S : Primitive + ' static ,
232234{
233235 let ( width, height) = image. dimensions ( ) ;
236+ // This is protection against a memory usage similar to #2340. See `vertical_sample`.
237+ assert ! (
238+ // Checks the implication: (width == 0) -> (height == 0)
239+ width != 0 || height == 0 ,
240+ "Unexpected prior allocation size. This case should have been handled by the caller"
241+ ) ;
242+
234243 let mut out = ImageBuffer :: new ( new_width, height) ;
235244 let mut ws = Vec :: new ( ) ;
236245
@@ -463,13 +472,24 @@ pub fn interpolate_bilinear<P: Pixel>(
463472// ```filter``` is the filter to use for sampling.
464473// The return value is not necessarily Rgba, the underlying order of channels in ```image``` is
465474// preserved.
475+ //
476+ // Note: if an empty image is passed in, panics unless the image is truly empty.
466477fn vertical_sample < I , P , S > ( image : & I , new_height : u32 , filter : & mut Filter ) -> Rgba32FImage
467478where
468479 I : GenericImageView < Pixel = P > ,
469480 P : Pixel < Subpixel = S > + ' static ,
470481 S : Primitive + ' static ,
471482{
472483 let ( width, height) = image. dimensions ( ) ;
484+
485+ // This is protection against a regression in memory usage such as #2340. Since the strategy to
486+ // deal with it depends on the caller it is a precondition of this function.
487+ assert ! (
488+ // Checks the implication: (height == 0) -> (width == 0)
489+ height != 0 || width == 0 ,
490+ "Unexpected prior allocation size. This case should have been handled by the caller"
491+ ) ;
492+
473493 let mut out = ImageBuffer :: new ( width, new_height) ;
474494 let mut ws = Vec :: new ( ) ;
475495
@@ -916,6 +936,16 @@ where
916936 I :: Pixel : ' static ,
917937 <I :: Pixel as Pixel >:: Subpixel : ' static ,
918938{
939+ // Check if there is nothing to sample from.
940+ let is_empty = {
941+ let ( width, height) = image. dimensions ( ) ;
942+ width == 0 || height == 0
943+ } ;
944+
945+ if is_empty {
946+ return ImageBuffer :: new ( nwidth, nheight) ;
947+ }
948+
919949 // check if the new dimensions are the same as the old. if they are, make a copy instead of resampling
920950 if ( nwidth, nheight) == image. dimensions ( ) {
921951 let mut tmp = ImageBuffer :: new ( image. width ( ) , image. height ( ) ) ;
@@ -970,6 +1000,11 @@ where
9701000 } ;
9711001
9721002 let ( width, height) = image. dimensions ( ) ;
1003+ let is_empty = width == 0 || height == 0 ;
1004+
1005+ if is_empty {
1006+ return ImageBuffer :: new ( width, height) ;
1007+ }
9731008
9741009 // Keep width and height the same for horizontal and
9751010 // vertical sampling.
@@ -1259,4 +1294,26 @@ mod tests {
12591294 let result = resize ( & image, 22 , 22 , FilterType :: Lanczos3 ) ;
12601295 assert ! ( result. into_raw( ) . into_iter( ) . any( |c| c != 0 ) ) ;
12611296 }
1297+
1298+ #[ test]
1299+ fn issue_2340 ( ) {
1300+ let empty = crate :: GrayImage :: from_raw ( 1 << 31 , 0 , vec ! [ ] ) . unwrap ( ) ;
1301+ // Really we're checking that no overflow / outsized allocation happens here.
1302+ let result = resize ( & empty, 1 , 1 , FilterType :: Lanczos3 ) ;
1303+ assert ! ( result. into_raw( ) . into_iter( ) . all( |c| c == 0 ) ) ;
1304+ // With the previous strategy before the regression this would allocate 1TB of memory for a
1305+ // temporary during the sampling evaluation.
1306+ let result = resize ( & empty, 256 , 256 , FilterType :: Lanczos3 ) ;
1307+ assert ! ( result. into_raw( ) . into_iter( ) . all( |c| c == 0 ) ) ;
1308+ }
1309+
1310+ #[ test]
1311+ fn issue_2340_refl ( ) {
1312+ // Tests the swapped coordinate version of `issue_2340`.
1313+ let empty = crate :: GrayImage :: from_raw ( 0 , 1 << 31 , vec ! [ ] ) . unwrap ( ) ;
1314+ let result = resize ( & empty, 1 , 1 , FilterType :: Lanczos3 ) ;
1315+ assert ! ( result. into_raw( ) . into_iter( ) . all( |c| c == 0 ) ) ;
1316+ let result = resize ( & empty, 256 , 256 , FilterType :: Lanczos3 ) ;
1317+ assert ! ( result. into_raw( ) . into_iter( ) . all( |c| c == 0 ) ) ;
1318+ }
12621319}
0 commit comments