99 lines
4.3 KiB
Rust
99 lines
4.3 KiB
Rust
//! Test enforcement of size and memory limits for animation decoding APIs.
|
|
|
|
use image::{io::Limits, AnimationDecoder, ImageDecoder, ImageResult};
|
|
|
|
#[cfg(feature = "gif")]
|
|
use image::codecs::gif::GifDecoder;
|
|
|
|
#[cfg(feature = "gif")]
|
|
fn gif_decode(data: &[u8], limits: Limits) -> ImageResult<()> {
|
|
let mut decoder = GifDecoder::new(data).unwrap();
|
|
decoder.set_limits(limits)?;
|
|
{
|
|
let frames = decoder.into_frames();
|
|
for result in frames {
|
|
result?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Checks that the function returned `ImageError::Limits`, panics otherwise
|
|
#[track_caller]
|
|
fn assert_limit_error(res: ImageResult<()>) {
|
|
let err = res.expect_err("The input should have been rejected because it exceeds limits");
|
|
match err {
|
|
image::ImageError::Limits(_) => (), // all good
|
|
_ => panic!("Decoding failed due to an error unrelated to limits"),
|
|
}
|
|
}
|
|
|
|
/// Each frame is the size of the image,
|
|
/// so we can just output each raw GIF frame buffer as the final composited frame
|
|
/// with no additional scratch space
|
|
#[test]
|
|
#[cfg(feature = "gif")]
|
|
fn animated_full_frame_discard() {
|
|
let data =
|
|
std::fs::read("tests/images/gif/anim/large-gif-anim-full-frame-replace.gif").unwrap();
|
|
|
|
let mut limits_dimensions_too_small = Limits::default();
|
|
limits_dimensions_too_small.max_image_width = Some(500);
|
|
limits_dimensions_too_small.max_image_height = Some(500);
|
|
assert_limit_error(gif_decode(&data, limits_dimensions_too_small));
|
|
|
|
let mut limits_memory_way_too_small = Limits::default();
|
|
// Start with a ridiculously low memory allocation cap
|
|
limits_memory_way_too_small.max_alloc = Some(5);
|
|
assert_limit_error(gif_decode(&data, limits_memory_way_too_small));
|
|
|
|
let mut limits_memory_too_small = Limits::default();
|
|
// 1000 * 1000 * 4 would be the exact size of the buffer for one RGBA frame.
|
|
// The decoder always peaks with at least two frames in memory at the same time.
|
|
// Set the limit a little higher than 1 frame than that it doesn't run into trivial checks
|
|
// for output frame size, and make it run into actual buffer allocation errors.
|
|
limits_memory_too_small.max_alloc = Some(1000 * 1000 * 5);
|
|
assert_limit_error(gif_decode(&data, limits_memory_too_small));
|
|
|
|
let mut limits_just_enough = Limits::default();
|
|
limits_just_enough.max_image_height = Some(1000);
|
|
limits_just_enough.max_image_width = Some(1000);
|
|
limits_just_enough.max_alloc = Some(1000 * 1000 * 4 * 2); // 4 for RGBA, 2 for 2 buffers kept in memory simultaneously
|
|
|
|
gif_decode(&data, limits_just_enough)
|
|
.expect("With these limits it should have decoded successfully");
|
|
}
|
|
|
|
/// The GIF frame does not cover the whole image, requiring additional scratch space
|
|
#[test]
|
|
#[cfg(feature = "gif")]
|
|
fn animated_frame_combine() {
|
|
let data = std::fs::read("tests/images/gif/anim/large-gif-anim-combine.gif").unwrap();
|
|
|
|
let mut limits_dimensions_too_small = Limits::default();
|
|
limits_dimensions_too_small.max_image_width = Some(500);
|
|
limits_dimensions_too_small.max_image_height = Some(500);
|
|
assert_limit_error(gif_decode(&data, limits_dimensions_too_small));
|
|
|
|
let mut limits_memory_way_too_small = Limits::default();
|
|
// Start with a ridiculously low memory allocation cap
|
|
limits_memory_way_too_small.max_alloc = Some(5);
|
|
assert_limit_error(gif_decode(&data, limits_memory_way_too_small));
|
|
|
|
let mut limits_memory_too_small = Limits::default();
|
|
// 1000 * 1000 * 4 * would be the exact size of two buffers for an RGBA frame.
|
|
// In this mode the decoder uses 2 full frames (accumulated result and the output frame)
|
|
// plus the smaller frame size from the GIF format decoder that it composites onto the output frame.
|
|
// So two full frames are not actually enough for decoding here.
|
|
// Verify that this is caught.
|
|
limits_memory_too_small.max_alloc = Some(1000 * 1000 * 4 * 2); // 4 for RGBA, 2 for 2 buffers kept in memory simultaneously
|
|
assert_limit_error(gif_decode(&data, limits_memory_too_small));
|
|
|
|
let mut limits_enough = Limits::default();
|
|
limits_enough.max_image_height = Some(1000);
|
|
limits_enough.max_image_width = Some(1000);
|
|
limits_enough.max_alloc = Some(1000 * 1000 * 4 * 3); // 4 for RGBA, 2 for 2 buffers kept in memory simultaneously
|
|
|
|
gif_decode(&data, limits_enough)
|
|
.expect("With these limits it should have decoded successfully");
|
|
}
|