Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for a wrong buffer size with an animated WEBP #1959

Merged
merged 1 commit into from
Jul 14, 2023

Conversation

arskov
Copy link

@arskov arskov commented Jul 13, 2023

In case of an animated WEBP image, which contains frames of a smaller size than an image's canvas, the buffer of a whole image is bigger than the buffer of the first frame. This results in a panic while trying to copy a frame buffer to an image buffer. This is applicable to a case when a user wants to create an image from a file or an in memory buffer like this:

let path = "/some/path/to/animated_transparent.webp"
let mut buffer = Vec::new();
let _ = std::fs::File::open(path)?.read_to_end(&mut buffer);
let img = image::load_from_memory(&buffer)?;
let size = img.dimensions();
let buffer = img.to_rgb8().into_raw();

This fix checks if the first frame is of a smaller size, and in this case draws a subimage to a canvas.
I have this issue in my personal case, but I believe it also fixes #1856.

I license past and future contributions under the dual MIT/Apache-2.0 license,
allowing licensees to choose either at their option.

@fintelia
Copy link
Contributor

The fuzz testing from the CI is showing some issues:


failures:

---- codecs::webp::encoder::tests::fuzz_webp_no_panic stdout ----
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at 'called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webp-0.2.4/src/encoder.rs:78:40
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at 'called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webp-0.2.4/src/encoder.rs:78:40
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at 'called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webp-0.2.4/src/encoder.rs:78:40
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at 'called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webp-0.2.4/src/encoder.rs:78:40
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at '[quickcheck] TEST FAILED (runtime error). Arguments: ([], 0, 0, 0)
Error: "called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION"', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/tester.rs:165:28


failures:
    codecs::webp::encoder::tests::fuzz_webp_no_panic

@arskov
Copy link
Author

arskov commented Jul 14, 2023

The fuzz testing from the CI is showing some issues:


failures:

---- codecs::webp::encoder::tests::fuzz_webp_no_panic stdout ----
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at 'called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webp-0.2.4/src/encoder.rs:78:40
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at 'called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webp-0.2.4/src/encoder.rs:78:40
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at 'called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webp-0.2.4/src/encoder.rs:78:40
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at 'called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/webp-0.2.4/src/encoder.rs:78:40
thread 'codecs::webp::encoder::tests::fuzz_webp_no_panic' panicked at '[quickcheck] TEST FAILED (runtime error). Arguments: ([], 0, 0, 0)
Error: "called `Result::unwrap()` on an `Err` value: VP8_ENC_ERROR_BAD_DIMENSION"', /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/tester.rs:165:28


failures:
    codecs::webp::encoder::tests::fuzz_webp_no_panic

I tried to run that tests on a master without my changes, and it produces the same errors with the same tests. I'll re-check, what would be a suggestion in this case?

@fintelia
Copy link
Contributor

Could you grab a backtrace of the failures? (By running with RUST_BACKTRACE=1)

If it isn't in the code being changed here, I'd say we just merge this PR and then figure out what's going wrong in a followup.

let (canvas_width, canvas_height) = self.dimensions();
if canvas_width == first_frame.width && canvas_height == first_frame.height {
first_frame.image.fill_buf(buf);
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slightly nervous that first_frame.width or first_frame.height could be larger than the size of the canvas. Do you know what would happen in that case?

Copy link
Author

@arskov arskov Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. If the frame's width or height are larger than the canvas size, or even if they are smaller, but they have an x or y offset + width/height, that gets larger than the canvas width/height, than the code will panic on extended.rs#L268, because of how Index is implemented for ImageBuffer.

We can make an additional check if the calculated index is within a canvas size extended.rs#266 and if some x or y is out of bounds, then break the corresponding loop. Some sort of crop of the frame if it is outside the canvas.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it isn't too much trouble, could you implement that cropping logic now? Otherwise it can wait for a followup PR

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created #1960, you can assign it to me. I'll implement it in a separate PR if you don't mind.

@arskov
Copy link
Author

arskov commented Jul 14, 2023

This is backtrace for tests from master (without the change)
https://gist.github.com/arskov/ef235460456cb29bc65cd25621be03bf

This is backtrace from the branch with the proposal
https://gist.github.com/arskov/df18c28a8c61031e11010e8a28bac3ab

Both show that the panic happens on the line below, so it looks like a separate issue to investigate
https://github.com/image-rs/image/blob/master/src/codecs/webp/encoder.rs#L106

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lossy WEBP with alpha: source slice length (761600) does not match destination slice length (997920)
2 participants