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

Read on uninitialized buffer may cause UB (2 functions) #400

Closed
JOE1994 opened this issue Jan 27, 2021 · 3 comments · Fixed by #401
Closed

Read on uninitialized buffer may cause UB (2 functions) #400

JOE1994 opened this issue Jan 27, 2021 · 3 comments · Fixed by #401
Labels
bug There is a bug. performance Affects the performance of the code.

Comments

@JOE1994
Copy link

JOE1994 commented Jan 27, 2021

Hello 🦀,
we (Rust group @sslab-gatech) found a memory-safety/soundness issue in this crate while scanning Rust code on crates.io for potential vulnerabilities.

Issue Description

fn read_string<R: Read>(
mut source: R,
buf: &mut Vec<u8>,
max_length: u64,
) -> StdResult<&str, StringError> {
let str_length = source.read_u16::<BE>().context(ReadLength)? as usize;
if str_length as u64 > max_length {
return Err(StringError::LengthOutOfBounds);
}
buf.clear();
buf.reserve(str_length);
unsafe { buf.set_len(str_length) };
source.read_exact(buf).context(ReadData)?;
from_utf8(buf).context(Validate)
}

buf.clear();
buf.reserve(len);
unsafe { buf.set_len(len) };
source.read_exact(&mut buf).context(ReadImageData)?;

llanfair::read_string() & llanfair::parse() creates an uninitialized buffer and passes it to user-provided Read implementation. This is unsound, because it allows safe Rust code to exhibit an undefined behavior (read from uninitialized memory).

This part from the Read trait documentation explains the issue:

It is your responsibility to make sure that buf is initialized before calling read. Calling read with an uninitialized buf (of the kind one obtains via MaybeUninit<T>) is not safe, and can lead to undefined behavior.

How to fix the issue?

The Naive & safe way to fix the issue is to always zero-initialize a buffer before lending it to a user-provided Read implementation. Note that this approach will add runtime performance overhead of zero-initializing the buffer.

As of Jan 2021, there is not yet an ideal fix that works in stable Rust with no performance overhead. Below are links to relevant discussions & suggestions for the fix.

@JOE1994 JOE1994 changed the title 2 functions allow custom Read on uninitialized buffer Read on uninitialized buffer may cause UB (2 functions) Jan 27, 2021
CryZe added a commit to CryZe/livesplit-core that referenced this issue Jan 27, 2021
For performance reasons we used to read into uninitialized buffers.
However this is only safe if you can prove that you won't ever read from
the uninitialized memory, which isn't possible in our case since we
accept any reader that implements `std::io::Read`. There's no guarantee
that the reader only fills our buffer and doesn't also try to read from
it. It's unlikely that any reader will actually do that, but there's a
slight possibility that we can't prove away. They are currently trying
to address this in std, so that you can safely express this. For now we
just fall back to zero initializing the buffer. The Llanfair parsing
should be rare enough that this shouldn't matter all too much anyway.
There are FIXMEs in the code now that someone can eventually fix once
the safe reading into unitialized buffers is possible.

Resolves LiveSplit#400
@CryZe
Copy link
Collaborator

CryZe commented Jan 27, 2021

Thanks, I was aware of this issue, but wasn't sure if I should wait for the safe solution or fix it. It's pretty unlikely that any Read impl actually reads from the buffer anyway. Anyways, the code isn't all that hot anyway, so it should be fine to zero initialize for now and later switch to the safe abstraction. Thanks for opening this, a PR is now up.

@CryZe CryZe added bug There is a bug. performance Affects the performance of the code. labels Jan 27, 2021
@JOE1994
Copy link
Author

JOE1994 commented Jan 27, 2021

@CryZe Thank you so much for your quick fix!
Would you also mind publishing a new release to crates.io? (maybe v0.11.1)

@CryZe
Copy link
Collaborator

CryZe commented Jan 27, 2021

0.11 is very outdated. I think I want to mostly finish #386 (basically done, just needs to be documented) and #372 and then publish v0.12.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug There is a bug. performance Affects the performance of the code.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants