@@ -221,6 +221,8 @@ pub(crate) fn box_kernel(_x: f32) -> f32 {
221
221
// ```new_width``` is the desired width of the new image
222
222
// ```filter``` is the filter to use for sampling.
223
223
// ```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.
224
226
fn horizontal_sample < P , S > (
225
227
image : & Rgba32FImage ,
226
228
new_width : u32 ,
@@ -231,6 +233,13 @@ where
231
233
S : Primitive + ' static ,
232
234
{
233
235
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
+
234
243
let mut out = ImageBuffer :: new ( new_width, height) ;
235
244
let mut ws = Vec :: new ( ) ;
236
245
@@ -463,13 +472,24 @@ pub fn interpolate_bilinear<P: Pixel>(
463
472
// ```filter``` is the filter to use for sampling.
464
473
// The return value is not necessarily Rgba, the underlying order of channels in ```image``` is
465
474
// preserved.
475
+ //
476
+ // Note: if an empty image is passed in, panics unless the image is truly empty.
466
477
fn vertical_sample < I , P , S > ( image : & I , new_height : u32 , filter : & mut Filter ) -> Rgba32FImage
467
478
where
468
479
I : GenericImageView < Pixel = P > ,
469
480
P : Pixel < Subpixel = S > + ' static ,
470
481
S : Primitive + ' static ,
471
482
{
472
483
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
+
473
493
let mut out = ImageBuffer :: new ( width, new_height) ;
474
494
let mut ws = Vec :: new ( ) ;
475
495
@@ -916,6 +936,16 @@ where
916
936
I :: Pixel : ' static ,
917
937
<I :: Pixel as Pixel >:: Subpixel : ' static ,
918
938
{
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
+
919
949
// check if the new dimensions are the same as the old. if they are, make a copy instead of resampling
920
950
if ( nwidth, nheight) == image. dimensions ( ) {
921
951
let mut tmp = ImageBuffer :: new ( image. width ( ) , image. height ( ) ) ;
@@ -970,6 +1000,11 @@ where
970
1000
} ;
971
1001
972
1002
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
+ }
973
1008
974
1009
// Keep width and height the same for horizontal and
975
1010
// vertical sampling.
@@ -1259,4 +1294,26 @@ mod tests {
1259
1294
let result = resize ( & image, 22 , 22 , FilterType :: Lanczos3 ) ;
1260
1295
assert ! ( result. into_raw( ) . into_iter( ) . any( |c| c != 0 ) ) ;
1261
1296
}
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
+ }
1262
1319
}
0 commit comments