Skip to content
New issue

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

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

Already on GitHub? # to your account

Decoder may expose contents of uninitialized memory in the output #33

Closed
Shnatsel opened this issue Jun 20, 2019 · 5 comments
Closed

Decoder may expose contents of uninitialized memory in the output #33

Shnatsel opened this issue Jun 20, 2019 · 5 comments

Comments

@Shnatsel
Copy link
Contributor

libflate might expose contents of uninitialized memory in the output when given a crafted input. This may be a devastating vulnerability in some contexts, e.g. if used as deflate backend for a PNG decoder. Details and impact analysis for similar bugs in PNG decoders in C can be found here.

I am confident that a private function is vulnerable, but I am not sure if this vulnerability can be exploiter by supplying a malformed input; there could be some non-local checks that prevent it.

I shall relay further details on the issue to the maintainer privately by email.

@sile
Copy link
Owner

sile commented Jun 30, 2019

I have received the email. Thanks!

@Shnatsel
Copy link
Contributor Author

Shnatsel commented Jul 4, 2019

This was fixed by #38. The bug is in the following code:

symbol::Symbol::Share { length, distance } => {
if self.buffer.len() < distance as usize {
return Err(invalid_data_error!(
"Too long backword reference: buffer.len={}, distance={}",
self.buffer.len(),
distance
));
}
let old_len = self.buffer.len();
self.buffer.reserve(length as usize);
unsafe {
self.buffer.set_len(old_len + length as usize);
let start = old_len - distance as usize;
let ptr = self.buffer.as_mut_ptr();
util::ptr_copy(
ptr.add(start),
ptr.add(old_len),
length as usize,
length > distance,
);
}

This code never checks that distance is not 0. If it is and length is > 0, the following happens:

  • self.buf gets unsafely grown to accommodate additional length elements and now contains uninitialized memory
  • util::ptr_copy gets called with parameters (old_len, old_len, length, true) which causes it to walk over the uninitialized memory region, reading every byte from it and immediately putting it back in its place
  • execution of the function ends, leaving self.buf with contents of uninitialized memory exposed in it

This may be a devastating vulnerability in some contexts, e.g. if used as deflate backend for a PNG decoder. Details and impact analysis for similar bugs in PNG decoders in C can be found here.

I am confident that this private function is vulnerable, but I am not sure if this vulnerability can be exploited by supplying a malformed input - there might be some non-local checks that prevent it.

Please fix the bug and check if it's possible to trigger it through a crafted input file, either via setting distance to 0 or via overflowing distance. If it is indeed possible, please file a security advisory at https://github.com/RustSec/advisory-db

@sile
Copy link
Owner

sile commented Aug 3, 2019

there might be some non-local checks that prevent it.

Yes.
The value of distance is detemined by Decoder::decode_distance method (https://github.com/sile/libflate/blob/master/src/deflate/symbol.rs#L224).
The method refers to DISTANCE_TABLE constant to look up the values of base (range: 1..=24_557) and extra_bits (range: 0..=13) variables, and returns base + ${read extra_bits from bits-stream) as the distance value.
So the minimum value of distance is 1, and the maximum value is 24_577 + ((1<<13) - 1) = 32_768 < std::u16::MAX.

@Shnatsel
Copy link
Contributor Author

Shnatsel commented Aug 4, 2019

Thanks! I'm closing this issue then.

@Shnatsel Shnatsel closed this as completed Aug 4, 2019
@sile
Copy link
Owner

sile commented Aug 4, 2019

Thank you very much for your contribution!

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

No branches or pull requests

2 participants