Skip to content
This repository has been archived by the owner on Mar 3, 2023. It is now read-only.

Heap buffer overflow on some images in v0.1.4 #10

Closed
Shnatsel opened this issue Jun 19, 2022 · 13 comments
Closed

Heap buffer overflow on some images in v0.1.4 #10

Shnatsel opened this issue Jun 19, 2022 · 13 comments

Comments

@Shnatsel
Copy link
Contributor

I've run zune-jpeg on an imageboard dump I obtained specifically for testing image decoders. Some of the images trigger a segmentation fault with a heap buffer overflow with the x86 feature enabled, or a panic with that feature disabled. A sample image triggering the issue is attached: 1637407.jpg

Code to reproduce:

use zune_jpeg::Decoder;
use std::fs::File;
use std::io::BufReader;

fn main() -> std::io::Result<()> {
    let path = std::env::args().skip(1).next().unwrap();
    let mut decoder = Decoder::new();
    decoder.decode_file(path);
    Ok(())
}

You can build it in release mode as normal and observe a segmentation fault, or you can build it with address sanitizer to get more details: RUSTFLAGS=-Zsanitizer=address cargo +nightly build --release --target=x86_64-unknown-linux-gnu
(replace "x86_64-unknown-linux-gnu" with your native platform)

@etemesi254
Copy link
Owner

Acknowledged.

This led to a big discovery, seems like we are failing on a lot of progressive jpeg images with some specific parameters like the one above.

Investigating and will hopefully land a fix in the next few days.

Also is it okay if this is added to the test bench?

@Shnatsel
Copy link
Contributor Author

Also is it okay if this is added to the test bench?

I'm not opposed to that, but I assume the image is copyrighted and I'm not the author, so I cannot grant this permission. So I think including it in a MIT OR Apache-2.0 repository is not possible.

@Shnatsel
Copy link
Contributor Author

Thank you for the fix!

I see that the commit marked as fixing this performs unchecked arithmetic - there is multiplication of untrusted values, which may overflow.

A heap buffer overflow in a JPEG decoder is a pretty serious security vulnerability. Could we introduce some additional bounds checks, possibly even at the cost of a few % of performance?

Memory safety is the primary selling point of Rust implementations of binary decoders, and I would like to take steps to make sure further violations of it cannot happen.

@Shnatsel
Copy link
Contributor Author

More specifically: since platform-independent code panicked instead of causing memory safety errors, it sounds prudent to add the same bounds checks to the x86-specific code.

@etemesi254
Copy link
Owner

I see that the commit marked as fixing this performs unchecked arithmetic - there is multiplication of untrusted values, which may overflow.

Should we check for multiplication overflow?? And panic if so??

A heap buffer overflow in a JPEG decoder is a pretty serious security vulnerability. Could we introduce some additional bounds checks, possibly even at the cost of a few % of performance?

I'm reopening this issue and will close it when I have added checks to unsafe code, I believe it's idct and upsampling that don't have checks.

For safety I have no issue with that, that part is parallelized so it should not see perf losses.

The only issue is that it sadly can't be propagated to the caller, since it's in another thread, so it means it's a hard assert, or maybe there is a way to pass errors to the upper caller using scoped thread pools?

@etemesi254 etemesi254 reopened this Jun 30, 2022
@Shnatsel
Copy link
Contributor Author

Should we check for multiplication overflow?? And panic if so??

This is only necessary if this value is later used for indexing into a slice without bounds checks. I believe the correct way to resolve this is to add bounds checks on indexing, instead of trying to check for overflow on every multiplication.

The only issue is that it sadly can't be propagated to the caller, since it's in another thread, so it means it's a hard assert, or maybe there is a way to pass errors to the upper caller using scoped thread pools?

Can the closure spawned on the thread pool return a value? If so, you can simply use that to propagate the result.

In case that's not possible, a scoped thread pool should be able to borrow some variables from the scope it is in, so you can simply set a variable in the parent thread's scope to Result::Ok<()> at the end of the execution from the worker thread, and initialize it with some error value. You will need one for each worker thread.

@etemesi254
Copy link
Owner

etemesi254 commented Jun 30, 2022 via email

@Shnatsel
Copy link
Contributor Author

If all else fails, you can always panic in the thread and then catch_unwind. This is even easier to do on the thread boundary when using the std thread spawning APIs.

FWIW the standard library support for scoped threads has been stabilized in 1.63, set to release 6 weeks from now.

But I'll take a panic over a heap buffer overflow any day, so an assert without any panic catching would still be a big improvement.

@etemesi254
Copy link
Owner

etemesi254 commented Jun 30, 2022 via email

@etemesi254
Copy link
Owner

etemesi254 commented Jul 1, 2022

@etemesi254
Copy link
Owner

Done

@Shnatsel
Copy link
Contributor Author

Shnatsel commented Jul 2, 2022

Thank you!

I'll keep running tests on my datasets and report any issues I find.

@etemesi254
Copy link
Owner

Appreciated

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

No branches or pull requests

2 participants