oden/third-party/vendor/image/tests/limits_anim.rs
2024-03-08 11:03:01 -08:00

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");
}